Official React hooks SDK for the Newsdata.io News
API. Drop-in useLatestNews, useArchiveNews, useCryptoNews,
useMarketNews, useNewsCount, … hooks plus a <NewsDataProvider> to share
one client. Built on the same proven core as the Node client — validation,
typed errors, retries with exponential backoff — and ships first-class
TypeScript definitions.
Zero runtime dependencies, no build step, React 18+ as a peer dependency.
npm install newsdataapi
# react is a peer dependency
npm install reactimport { NewsDataProvider, useLatestNews } from 'newsdataapi';
function Headlines() {
const { data, error, isLoading } = useLatestNews({
q: 'bitcoin',
country: ['us', 'gb'],
language: 'en',
});
if (isLoading) return <p>Loading…</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{data.results.map((article) => (
<li key={article.article_id}>
<a href={article.link}>{article.title}</a>
</li>
))}
</ul>
);
}
export default function App() {
return (
<NewsDataProvider apiKey={process.env.REACT_APP_NEWSDATA_API_KEY}>
<Headlines />
</NewsDataProvider>
);
}| Hook | Endpoint | Notes |
|---|---|---|
useLatestNews(params) |
/1/latest |
Real-time news |
useArchiveNews(params) |
/1/archive |
Historical news |
useNewsSources(params) |
/1/sources |
Available sources (single page) |
useCryptoNews(params) |
/1/crypto |
Cryptocurrency news |
useMarketNews(params) |
/1/market |
Market / financial news |
useNewsCount(params) |
/1/count |
Aggregate counts (requires from_date, to_date) |
useCryptoCount(params) |
/1/crypto/count |
Aggregate crypto counts |
useMarketCount(params) |
/1/market/count |
Aggregate market counts |
useNewsStream(registrationId) |
wss://ws.newsdata.io/ws/event |
Real-time stream (below) |
Every hook has the same shape:
const { data, error, isLoading, refetch } = useLatestNews(params, options);data— the API response (nulluntil the first fetch resolves)error— a typedNewsdataError(nullon success)isLoading—truewhile a request is in flightrefetch()— re-run the request; returns the underlyingPromise
useLatestNews(params, { enabled: false }) // skip the request until enabledoptions.enabled (default true) defers fetching — handy when params aren't
ready (e.g. waiting on user input).
Values can be a single string or an array of strings (sent comma-joined),
and parameter names are case-insensitive — qInTitle and qintitle are
equivalent:
useLatestNews({ country: ['us', 'gb'], language: 'en', size: 20 });Inline param objects are safe — the hook compares by value, not reference, so re-renders only re-fetch when the values change.
useNewsStream opens a real-time connection for a registered query and
accumulates the matching articles. Register the query first — the returned
registration_id identifies it from then on:
import { useEffect, useState } from 'react';
import { useNewsDataClient, useNewsStream } from 'newsdataapi';
function BitcoinTicker() {
const client = useNewsDataClient();
const [registrationId, setRegistrationId] = useState(null);
useEffect(() => {
client.websocketRegister({ q: 'bitcoin', language: 'en' })
.then(({ results }) => setRegistrationId(results.registration_id));
}, [client]);
// A falsy id defers connecting until registration resolves.
const { articles, error, isConnected } = useNewsStream(registrationId);
if (error) return <p>Stream error: {error.message}</p>;
return (
<>
<p>{isConnected ? 'live' : 'connecting…'}</p>
<ul>
{articles.map((a) => <li key={a.article_id}>{a.title}</li>)}
</ul>
</>
);
}The hook returns { articles, latest, error, isConnected }. Articles
accumulate newest first and are capped at maxArticles (default 100) so a
long-lived stream can't grow without bound. The connection opens on mount,
closes on unmount, and reconnects through a capped exponential backoff.
const { articles, latest } = useNewsStream(registrationId, {
enabled: true, // defer connecting when false
maxArticles: 100, // cap on retained articles
reconnect: true, // auto-reconnect on transient drops
});error is set for a permanent rejection — bad API key or unknown registration_id,
exhausted API credits, or too many simultaneous devices — which surfaces as NewsdataWebSocketAuthError and is not retried.
The server always accepts the handshake and then closes with code 1008 when
the connection is refused, carrying one of three reasons: invalid credentials or registration not found, api limit reached, or device limit reached (more
than 5 devices on one registration_id). Every other close code — including
1013 (send timeout, meaning the client read too slowly) — is transient and
reconnects.
Each delivered article consumes 1 API credit per connected device.
Transient drops reconnect silently and leave error null.
Managing registered queries goes through the client:
websocketRegister(params), websocketFetch(), and
websocketDelete(registrationId). Registering an identical query twice
rejects with a NewsdataApiError whose statusCode is 409; the existing id is
at err.responseBody.results.registration_id.
Outside React, use the same NewsDataApiWebSocket class the hook wraps:
import { NewsDataApiWebSocket } from 'newsdataapi';
const ws = new NewsDataApiWebSocket(client);
for await (const response of ws.stream(registrationId)) {
console.log(response.results);
}Two ways to wire it up:
// 1. Let the provider construct the client.
<NewsDataProvider apiKey="..." options={{ timeout: 10_000, maxRetries: 3 }}>
<App />
</NewsDataProvider>
// 2. Or pass your own pre-built client (full control over its lifecycle).
import { NewsDataApiClient } from 'newsdataapi';
const client = new NewsDataApiClient(apiKey);
<NewsDataProvider client={client}>
<App />
</NewsDataProvider>Anywhere inside the provider you can grab the client directly:
import { useNewsDataClient } from 'newsdataapi';
function ExportButton() {
const client = useNewsDataClient();
return <button onClick={() => client.archiveApi({ q: 'x' }).then(save)}>Export</button>;
}All hook errors are instances of the typed hierarchy from the core SDK:
import {
NewsdataValidationError, NewsdataAuthError, NewsdataRateLimitError,
NewsdataApiError, NewsdataNetworkError,
} from 'newsdataapi';
if (error instanceof NewsdataRateLimitError) {
console.log('retry after', error.retryAfter, 'seconds');
}NewsdataError (catch-all base)
├── NewsdataValidationError (.param)
├── NewsdataApiError (.statusCode, .responseBody)
│ ├── NewsdataAuthError (401 / 403)
│ ├── NewsdataRateLimitError (429; .retryAfter)
│ └── NewsdataServerError (5xx)
├── NewsdataNetworkError (.cause)
└── NewsdataWebSocketError (real-time stream)
└── NewsdataWebSocketAuthError (policy-violation close 1008)
Validation errors are thrown before the request is sent (no API quota
spent) — e.g. setting q and qInTitle together, an unsupported parameter
for that endpoint, or missing from_date/to_date on a count endpoint.
You can also use the underlying client without React — same surface as
newsdata-nodejs-client:
import { NewsDataApiClient } from 'newsdataapi';
const client = new NewsDataApiClient(apiKey, { timeout: 10_000 });
// scroll: follow nextPage cursors and merge.
const merged = await client.latestApi({ q: 'news', scroll: true, maxResult: 200 });
// paginate: async generator, one page at a time.
for await (const page of client.latestApi({ q: 'news', paginate: true, maxPages: 5 })) {
console.log(page.results.length);
}npm install
npm test # node:test, 34 tests, no API key requiredOfficial Newsdata.io clients across languages and runtimes:
- Python — newsdataapi/python-client (PyPI)
- Node.js — newsdataapi/newsdata-nodejs-client (npm)
- PHP — newsdataapi/php-client (Packagist)
- Java — newsdataapi/newsdata-java-sdk (Maven Central)
- .NET — newsdataapi/newsdata-dotnet-sdk (NuGet)
- Go — newsdataapi/newsdata-go-client (pkg.go.dev)
- Dart / Flutter — newsdataapi/newsdata-flutter-client (pub.dev)
- MCP Server (AI assistants) — newsdataapi/newsdata.io-mcp (PyPI)
Also see free news datasets for ML / NLP work.
