Skip to content

Journalists

The Journalists endpoints provide a searchable directory of journalists (article bylines), normalized by name — bylines appearing across multiple outlets are merged into a single record with an outlets array. Profiles include a coverage block (activity, topics, who they cover) and the journalist's latest articles.

Normalization caveat

Journalists are merged by exact name. This means distinct people sharing a name, and generic bylines (e.g. admin, Redaktion), may be merged into one record. Stronger disambiguation is planned for a future release.

Added in 1.0.42

Full response field definitions are in API Response Structure. See also the Find journalists on a beat recipe.

Endpoints

GET /v1/journalists          # list & search
GET /v1/journalists/{id}     # single journalist profile

Authentication via the X-API-Key header or the api_key query parameter.

GET /v1/journalists

Query Parameters

ParameterTypeRequiredDescription
namestringNoFilter by journalist name (substring match).
pageintegerNoPage number (default 1).
per_pageintegerNoResults per page (default 100, max 250; the plan ceiling applies — Free 10, Starter 50, Basic 200).
api_keystringYes*Your API key. Can also be provided via headers.

* Required if not provided in headers

Example

bash
curl "https://api.apitube.io/v1/journalists?name=Alex&per_page=5&api_key=YOUR_API_KEY"
python
import requests

resp = requests.get(
    "https://api.apitube.io/v1/journalists",
    params={"name": "Alex", "per_page": 5, "api_key": "YOUR_API_KEY"},
)
print(resp.json())
javascript
const params = new URLSearchParams({ name: "Alex", per_page: "5", api_key: "YOUR_API_KEY" });
const resp = await fetch(`https://api.apitube.io/v1/journalists?${params}`);
console.log(await resp.json());
php
$query = http_build_query(["name" => "Alex", "per_page" => 5, "api_key" => "YOUR_API_KEY"]);
$data = json_decode(file_get_contents("https://api.apitube.io/v1/journalists?$query"), true);
print_r($data);
go
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
)

func main() {
	u, _ := url.Parse("https://api.apitube.io/v1/journalists")
	q := u.Query()
	q.Set("name", "Alex")
	q.Set("per_page", "5")
	q.Set("api_key", "YOUR_API_KEY")
	u.RawQuery = q.Encode()

	resp, _ := http.Get(u.String())
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	var data map[string]any
	json.Unmarshal(body, &data)
	fmt.Println(data)
}
java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example {
    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.apitube.io/v1/journalists?name=Alex&per_page=5&api_key=YOUR_API_KEY"))
            .GET()
            .build();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());
    }
}
text
Write a script in your preferred language that calls the APITube News API:

GET https://api.apitube.io/v1/journalists?name=Alex&per_page=5

Read the API key from an environment variable (do not hardcode it), handle request
errors, and print the key fields of each result.
Docs: https://docs.apitube.io/platform/news-api/everything
json
{
  "status": "ok",
  "limit": 5,
  "page": 1,
  "has_next_pages": true,
  "results": [
    {
      "id": 15898,
      "name": "Alex Weiner",
      "outlets": [
        { "id": 354, "name": "ktar.com", "domain": "ktar.com" },
        { "id": 1277, "name": "arizonasports.com", "domain": "arizonasports.com" }
      ],
      "outlet_count": 2,
      "links": {
        "self": "https://api.apitube.io/v1/journalists/15898",
        "articles": "https://api.apitube.io/v1/news/everything?author.id=15898"
      }
    }
  ]
}

Journalist profile

GET /v1/journalists/{id}

Returns a journalist with the outlets they publish under (merged by name, capped at 500 records), a coverage block, and recent_articles.

recent_articles holds the 5 latest articles, each a complete article object — same shape as /v1/news/everything, body and full entities included — so the response is far larger than the field list suggests.

Query Parameters

ParameterTypeRequiredDescription
coveragestringNoSet to false to omit the coverage block.
api_keystringYes*Your API key.
json
{
  "id": 15898,
  "name": "Alex Weiner",
  "outlets": [
    { "id": 1277, "name": "arizonasports.com", "domain": "arizonasports.com" },
    { "id": 354, "name": "ktar.com", "domain": "ktar.com" }
  ],
  "links": { "self": "...", "articles": "..." },
  "coverage": {
    "article_count": 265,
    "first_seen": "2025-06-24",
    "last_seen": "2026-05-22",
    "sentiment": { "positive": 61, "neutral": 0, "negative": 30 },
    "momentum": { "last_30_days": 0, "previous_30_days": 0, "change_pct": null },
    "timeline": [ { "period": "2025-06-01", "count": 1 } ],
    "top_topics": [ { "id": "industry.financial_news", "name": "Finance Industry News", "count": 91 } ],
    "top_entities": [ { "id": 1480590, "name": "University of Arizona", "count": 248 } ],
    "top_countries": [ { "id": 102, "name": "United States", "code": "us", "count": 265 } ],
    "top_languages": [ { "id": 1, "name": "English", "code": "en", "count": 265 } ]
  },
  "recent_articles": [ { "id": 3074512345, "title": "...", "href": "...", "published_at": "...", "body": "...", "entities": [], "categories": [], "sentiment": {} } ]
}

links.articles points at ?author.id=<this id>one author record, not the merged set. For a journalist published under several outlets the link therefore returns fewer articles than coverage.article_count: for author 570 the profile reports 412 articles while author.id=570 matches 191. The author.id filter accepts at most three ids, so a merged link is not expressible for a name with more records than that.

author.name is not a shortcut either: it resolves the name to a single author record (the first match) rather than all of them — author.name=Alex Weiner matches 3 articles where the profile reports 265. Use coverage for totals and the article ids from recent_articles when you need the merged set.

Coverage fields

FieldDescription
article_countTotal articles authored across every author record that shares this name (a journalist writing for three outlets has three author ids; the profile merges them, up to 500). This is why it is larger than what links.articles returns — see below.
first_seen / last_seenDate range of activity.
sentimentArticle counts by overall polarity.
momentumArticles in the last 30 days vs. the previous 30 days.
timelineMonthly article counts.
top_topicsTopics the journalist writes about most.
top_entitiesEntities (people, companies, places) the journalist covers most.
top_countries / top_languagesGeographic and language spread of their articles.
recent_articlesThe journalist's latest articles.

Pricing

Each request costs 1 point, charged only when the response contains at least one result. Zero-result searches are free. Requests with no remaining credits return 402.

Errors

A request for a non-existent journalist returns 404 with code ER0151 (Journalist not found.).