newsdataio / newsdataapi
Official PHP client (SDK) for the Newsdata.io News API — fetch real-time, historical, crypto, and stock-market news via REST with validation, retries, and error handling.
Requires
- php: ^7.3 || ^8.0
- ext-curl: *
- ext-json: *
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.50
- phpstan/phpstan: ^1.10 || ^2.0
- phpunit/phpunit: ^9 || ^10
Suggests
- phrity/websocket: Required for real-time streaming via NewsdataWebSocket::stream() (PHP 8.1+)
- psr/log: Allows attaching a PSR-3 logger via NewsdataApi::setLogger()
Provides
None
Conflicts
None
Replaces
None
README
Newsdata.io PHP Client
The official PHP client for the Newsdata.io REST API. It
wraps every endpoint (latest, archive, sources, crypto, market,
count, crypto/count, market/count) with client-side parameter validation,
automatic retries with exponential backoff, and a typed exception hierarchy. It
also covers the real-time WebSocket service: register, list, and delete queries,
and stream the matching news as it is published.
Requirements
PHP 7.3+ with the curl and json extensions.
Installation
With Composer:
composer require newsdataio/newsdataapi
Without Composer, include the bundled autoloader:
require_once '/path/to/php-client/autoload.php';
Quickstart
use NewsdataIO\NewsdataApi; use NewsdataIO\Exception\NewsdataException; $client = new NewsdataApi(NEWSDATA_API_KEY); try { $response = $client->get_latest_news([ 'q' => 'bitcoin', 'country' => ['us', 'gb'], // string or array of strings 'language' => 'en', ]); foreach ($response->results as $article) { echo $article->title, PHP_EOL; } } catch (NewsdataException $e) { echo 'Request failed: ', $e->getMessage(), PHP_EOL; }
Pass ['language' => ['en', 'fr']] and the array is sent comma-separated.
By default the response is decoded to objects; call
$client->setDecodeJsonAsArray(true) to get associative arrays instead.
Endpoints
| Method | Endpoint | Notes |
|---|---|---|
get_latest_news($data) |
/1/latest |
Real-time news |
news_archive($data) |
/1/archive |
Historical news |
news_sources($data) |
/1/sources |
Available sources |
get_crypto_news($data) |
/1/crypto |
Cryptocurrency news |
get_market_news($data) |
/1/market |
Market / financial news |
get_news_count($data) |
/1/count |
Aggregate counts (requires from_date, to_date) |
get_crypto_count($data) |
/1/crypto/count |
Aggregate crypto counts (requires dates) |
get_market_count($data) |
/1/market/count |
Aggregate market counts (requires dates) |
get_websocket_register($data) |
/1/websocket/register |
Register a real-time query |
get_websocket_fetch() |
/1/websocket/fetch |
List registered queries |
get_websocket_delete($id) |
/1/websocket/delete |
Delete a registered query |
Each $data value may be a single string or an array of strings. Parameter
names are case-insensitive. See the
Newsdata.io documentation — or the
OpenAPI 3.1 spec — for the full
parameter reference per endpoint.
$client->get_market_news(['q' => 'apple', 'market_id' => 'AAPL']); $client->get_news_count([ 'from_date' => '2024-01-01', 'to_date' => '2024-01-31', 'interval' => 'day', ]);
Raw query
To pass a query string or full URL verbatim, use raw_query. It is mutually
exclusive with every other parameter and is validated against the endpoint's
allowed keys:
$client->get_latest_news(['raw_query' => 'q=bitcoin&country=us&language=en']);
Client-side validation
Before any request is sent, parameters are validated and normalized. A
NewsdataValidationError is raised (without spending API quota) when:
- a parameter is not accepted by that endpoint;
- mutually-exclusive parameters are set together —
q/qInTitle/qInMeta,country/excludecountry,category/excludecategory,language/excludelanguage,domain/domainurl/excludedomain; sizeis outside 1–50;sentiment_scoreis set withoutsentiment;- a count endpoint is missing
from_dateorto_date.
Booleans (full_content, image, video, removeduplicate) are coerced to
1 / 0.
Real-time news (WebSocket)
Register a query first — the returned registration_id identifies it from then on:
use NewsdataIO\NewsdataApi; use NewsdataIO\NewsdataWebSocket; $api = new NewsdataApi('YOUR_API_KEY'); $ws = new NewsdataWebSocket($api); $registered = $ws->register(['q' => 'bitcoin', 'language' => 'en']); $registrationId = $registered->results->registration_id;
register() takes the familiar filter names (q, country, language,
domain, …) — no date or paging filters, since a registered query matches news
as it is published. Registering an identical query twice throws
NewsdataAPIError with status 409; the existing id is in the response body.
fetch() lists every registered query and delete($id) removes one. All three
also exist directly on the API object as get_websocket_register(),
get_websocket_fetch() and get_websocket_delete().
Then stream. stream() is a generator — break out of the loop to stop, and
the connection closes for you:
foreach ($ws->stream($registrationId) as $response) { foreach ($response->results as $article) { echo $article->title, ' - ', $article->link, PHP_EOL; } }
Transient drops (network errors, server restarts, abnormal closes) are
reconnected automatically with a capped exponential backoff. Pass
'reconnect' => false to stop on the first disconnect instead. A permanent
rejection — bad API key or unknown
registration_id, exhausted API credits, or too many simultaneous devices — throws
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.
Catch it like any other client error:
use NewsdataIO\Exception\NewsdataWebSocketAuthError; use NewsdataIO\Exception\NewsdataWebSocketError; try { foreach ($ws->stream($registrationId) as $response) { // ... } } catch (NewsdataWebSocketAuthError $e) { echo 'rejected: ', $e->getMessage(), PHP_EOL; } catch (NewsdataWebSocketError $e) { echo 'stream error: ', $e->getMessage(), PHP_EOL; }
All connection options are optional:
$ws = new NewsdataWebSocket($api, [ 'baseUrl' => 'wss://ws.newsdata.io/ws/event', // staging / self-hosted 'reconnect' => true, // auto-reconnect on transient drops; default true 'reconnectDelay' => 1.0, // seconds before the first reconnect (doubles each retry) 'reconnectDelayMax' => 30.0, // cap on the reconnect delay 'handshakeTimeout' => 10, // seconds to wait for the opening handshake ]);
Streaming needs one extra package. PHP has no WebSocket client in core, so
stream()requiresphrity/websocket(PHP 8.1+):composer require phrity/websocketIt is an optional dependency — everything else in this SDK, including the three
websocket/*management endpoints above, works without it on every supported PHP version.stream()throws aNewsdataWebSocketErrortelling you to install it if it is missing.
Runnable example: examples/websocket.php.
Error handling
use NewsdataIO\Exception\NewsdataValidationError; use NewsdataIO\Exception\NewsdataAuthError; use NewsdataIO\Exception\NewsdataRateLimitError; use NewsdataIO\Exception\NewsdataAPIError; use NewsdataIO\Exception\NewsdataNetworkError; try { $client->get_latest_news(['q' => 'news']); } catch (NewsdataValidationError $e) { // bad parameter — $e->getParam() } catch (NewsdataAuthError $e) { // 401 / 403 } catch (NewsdataRateLimitError $e) { // 429 — $e->getRetryAfter() } catch (NewsdataAPIError $e) { // other API error — $e->getStatusCode(), $e->getResponseBody() } catch (NewsdataNetworkError $e) { // cURL / connectivity failure }
Hierarchy (all under the NewsdataIO\Exception namespace):
NewsdataException (catch-all base)
├── NewsdataValidationError (getParam())
├── NewsdataAPIError (getStatusCode(), getResponseBody())
│ ├── NewsdataAuthError (401 / 403)
│ ├── NewsdataRateLimitError (429; getRetryAfter())
│ └── NewsdataServerError (5xx)
├── NewsdataNetworkError (cURL / connectivity)
└── NewsdataWebSocketError (real-time stream)
└── NewsdataWebSocketAuthError (policy-violation close 1008)
Configuration
$client->setTimeouts($connectSeconds = 10, $totalSeconds = 30); $client->setRetries($maxAttempts = 5, $backoffBaseSeconds = 2.0); $client->setRetryBackoffMax($seconds = 60.0); $client->setDecodeJsonAsArray(true); $client->setProxy([ 'CURLOPT_PROXY' => 'proxy.example.com', 'CURLOPT_PROXYPORT' => 8080, 'CURLOPT_PROXYUSERPWD' => 'user:pass', ]); $client->setLogger($psr3Logger); // API key is redacted from logged URLs
Retries cover network errors, HTTP 429, and 5xx responses. 429 honors the
Retry-After header (integer seconds or HTTP-date); otherwise backoff is
exponential (2s → 4s → 8s …, capped). Auth and other 4xx errors are never
retried.
Response metadata for the most recent call:
$client->getLastResponse()->getHttpCode(); $client->getLastResponse()->getHeaders();
Development
composer install composer test # or: vendor/bin/phpunit
The test suite (tests/) covers the parameter validator and runs entirely
offline — no API key required.
Related libraries
Official Newsdata.io clients across languages and runtimes:
- Python — newsdataapi/python-client (PyPI)
- Node.js — newsdataapi/newsdata-nodejs-client (npm)
- React (hooks) — newsdataapi/newsdata-reactjs-client (npm)
- 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.
License
MIT.
