apitube / news-api
PHP SDK for the APITube News API
Requires
- php: ^8.1
- php-http/discovery: ^1.20
- psr/http-client: ^1.0
- psr/http-client-implementation: *
- psr/http-factory: ^1.0
- psr/http-factory-implementation: *
Requires (Dev)
- guzzlehttp/guzzle: ^7.9
- php-http/mock-client: ^1.6
- phpunit/phpunit: ^10.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
PHP SDK for the APITube News API — access global news articles, headlines, stories, sentiment analysis, and more.
Requirements
- PHP 8.1+
- A PSR-18 HTTP client (e.g. Guzzle)
- A PSR-17 HTTP factory
Installation
composer require apitube/news-api
Quick Start
use APITube\Client; $client = new Client(apiKey: 'your-api-key'); // Search news articles $response = $client->news('everything', [ 'title' => 'artificial intelligence', 'language.code' => 'en', 'per_page' => 5, ]); foreach ($response->articles as $article) { echo $article->title . "\n"; echo $article->url . "\n\n"; }
Usage
Initialize the client
use APITube\Client; $client = new Client( apiKey: 'your-api-key', baseUrl: 'https://api.apitube.io', // optional, default value );
You can pass any PSR-18 HTTP client:
$client = new Client( apiKey: 'your-api-key', httpClient: new \GuzzleHttp\Client(['timeout' => 30]), );
Search articles
$response = $client->news('everything', [ 'title' => 'climate change', 'language.code' => 'en', 'per_page' => 10, ]); echo "Page: {$response->page}\n"; echo "Has next page: " . ($response->hasNextPages ? 'yes' : 'no') . "\n"; foreach ($response->articles as $article) { echo "{$article->title}\n"; echo "Source: {$article->source?->domain}\n"; echo "Sentiment: {$article->sentiment?->overall?->polarity}\n"; // English translation of the headline for non-English articles // (null for English articles — fall back to the original title) echo "English title: " . ($article->translations?->en?->title ?? $article->title) . "\n\n"; }
Search in plain language
Instead of assembling filters by hand, describe what you want in the prompt parameter. The API
translates the sentence into the regular filters before searching and returns what it used in
meta.prompt:
$response = $client->news('everything', [ 'prompt' => 'Tesla and Elon Musk news in English for the last 10 days', 'per_page' => 5, ]); // ['person.name' => 'Elon Musk', 'organization.name' => 'Tesla', 'language.code' => 'en', 'published_at.start' => 'NOW-10DAY'] print_r($response->meta['prompt']['applied']); print_r($response->meta['prompt']['ignored']); // values understood but not used, each with a reason var_dump($response->meta['prompt']['cached']); // true = served from cache, no extra charge
The prompt must be 3–500 characters. Filters you pass yourself always win over the prompt.
Translating a prompt costs 2 extra points, but only the first time a given wording is used —
interpretations are cached for 24 hours. See the
prompt reference.
Specify API version
$response = $client->news('everything', [ 'title' => 'artificial intelligence', 'per_page' => 5, ], version: 'v1');
By default, the SDK uses v1.
Top headlines
$response = $client->news('top-headlines', [ 'language.code' => 'en', 'per_page' => 10, ]); foreach ($response->articles as $article) { echo "{$article->title} — {$article->source?->domain}\n"; }
Get a single article
$response = $client->news('article', [ 'id' => 'article-id', ]); $article = $response->articles[0]; echo $article->title . "\n"; echo $article->body . "\n";
Get articles by story
$response = $client->news('story', [ 'id' => 'story-id', ]); foreach ($response->articles as $article) { echo "{$article->title}\n"; }
Raw articles
Fetch recently discovered articles before parsing and enrichment:
$response = $client->news('raw', [ 'per_page' => 50, 'sort.by' => 'published_at', 'sort.order' => 'desc', ]); foreach ($response->articles as $article) { echo "{$article->title}\n"; }
Count articles
Count articles matching the same filters as everything:
$count = $client->count([ 'title' => 'artificial intelligence', 'language.code' => 'en', ]); echo "Matching articles: {$count}\n";
Autocomplete suggestions
Supported types: categories, topics, industries, entities.
$items = $client->suggest('categories', 'spo'); foreach ($items as $item) { echo "{$item['name']} (id: {$item['id']})\n"; }
Reference data (people, companies, journalists)
Each entity exposes a paginated list method and a profile method by ID:
// List $people = $client->people(['name' => 'Elon', 'per_page' => 5]); foreach ($people->results as $person) { echo "{$person['name']} (id: {$person['id']})\n"; } // Profile with coverage statistics $profile = $client->person($people->results[0]['id']); echo "Articles: {$profile['coverage']['article_count']}\n"; // Same shape for the other entities: $client->companies(['name' => 'Tesla']); $client->company($id); $client->journalists(['name' => 'Smith']); $client->journalist($id);
Check balance
$balance = $client->balance(); echo "Plan: {$balance->plan}\n"; echo "Points: {$balance->points}\n";
Ping
$isAvailable = $client->ping(); echo $isAvailable ? 'API is available' : 'API is unavailable';
Error Handling
The SDK throws typed exceptions:
use APITube\Exceptions\ApiException; use APITube\Exceptions\AuthenticationException; use APITube\Exceptions\RateLimitException; try { $response = $client->news('everything', ['title' => 'php']); } catch (AuthenticationException $e) { // Invalid or missing API key (HTTP 401) echo "Auth error: {$e->getMessage()}\n"; } catch (RateLimitException $e) { // Rate limit exceeded (HTTP 429) echo "Rate limited. Retry after: {$e->retryAfter} seconds\n"; } catch (ApiException $e) { // Other API errors echo "API error ({$e->getCode()}): {$e->getMessage()}\n"; echo "Request ID: {$e->requestId}\n"; }
Testing
composer install vendor/bin/phpunit
License
MIT