Skip to content

Migrating from the GDELT DOC 2.0 API

Replace api.gdeltproject.org/api/v2/doc/doc with /v1/news/everything. Unlike every other guide in this section, both sides of every claim below were executed rather than read — GDELT needs no key, so its behaviour could be measured directly.

These are not the same kind of product

GDELT is a free research dataset. APITube is a commercial news API. The difference decides whether you should migrate at all.

GDELT DOC 2.0APITube
Key requiredNoYes
CostFreePaid, free tier available
Rate limitEffectively a handful per hour — see below50/minute paid, 10/minute free
Records per request250250
PagingNonepage
Search scopeFull article text, 65+ languagesHeadlines
Article bodyNot returnedbody and body_html
Fields per article8Dozens
ErrorsHTTP 200 with a plain-text bodyJSON with a status and an error code

Move to APITube for: the article text, commercial licensing, entity extraction, sentiment, and a rate limit that supports production traffic.

Stay on GDELT for: 65-language full-text reach, the GKG themes taxonomy, and tone timelines — on a zero budget. Nothing here replaces GDELT's global research coverage.

Your first request

bash
# GDELT — no key needed
curl "https://api.gdeltproject.org/api/v2/doc/doc?query=tesla&mode=artlist&format=json&maxrecords=250"
bash
# APITube
curl "https://api.apitube.io/v1/news/everything?title=tesla&per_page=250" \
  -H "X-API-Key: YOUR_API_KEY"

GDELT reports errors with HTTP 200

This is the trap most likely to be hiding in your current code. Ask for more than the maximum:

bash
curl "https://api.gdeltproject.org/api/v2/doc/doc?query=tesla&mode=artlist&format=json&maxrecords=251"
# HTTP 200
# A maximum of 250 records can be returned.

Status 200, body is prose. A client that checks the status and then parses JSON passes the first test and throws on the second. Rate limiting behaves identically — the body is a sentence, not a JSON error.

If a try/except around JSON parsing treats a parse failure as "no results", it has been silently swallowing both conditions. APITube returns 400 ER0171 with a structured error instead, so that code path can go — but look at what it was hiding first.

The rate limit is the real migration cost

GDELT's throttle message says "one request every 5 seconds". Measured on 27 July 2026:

Interval between requestsRequests that got through
6 seconds1 of 7
16 seconds4 of 12
60 seconds3 of 8
150 seconds0 of 3
300 seconds, over 35 minutes0 of 7

Backing off made it worse. Roughly 60 requests spread over 90 minutes was enough to trigger a block that no useful retry interval cleared, and a quiet period that cleared it once failed to clear it later in the same session.

The instinct on 429 — retry more slowly — is exactly what keeps you blocked here. APITube's 429 is a real status code against a documented per-minute budget, so backoff behaves normally.

For a backfill job, this ratio rather than the field mapping is what changes your runtime.

Parameter mapping

GDELTAPITubeNote
query (bare terms)titleFull text → headlines
query domain:Xsource.domain=XExact
query sourcelang:englishlanguage.code=enName → ISO code
query sourcecountry:USsource.country.code=usName → ISO code
query theme:XNo GKG taxonomy
maxrecordsper_pageBoth cap at 250
mode=artlist(the default)
mode=timelinevol/v1/news/trendsDifferent endpoint and shape
mode=tonechartCompute from sentiment.overall.score
pageGDELT has no paging at all

The full table is in the migration kit, including the parameters it deliberately leaves unmapped because they could not be verified.

Names become codes, and that breaks naive ports

GDELT emits English names; APITube takes ISO codes.

GDELT:    "language": "Serbian",  "sourcecountry": "Serbia"
APITube:  language.code=sr        source.country.code=rs

Forwarding the name fails, and the error tells you which mistake you made:

What you sendResponse
language.code=Serbian400 ER0061 — must be 1–2 characters
language.code=zz400 ER0237 — not found
source.country.code=Serbia400 ER0246 — must be 2 characters
source.country.code=zz400 ER0212 — not found

A length error means a GDELT value was forwarded unconverted — a bug in your mapping code. A not-found error means your table produced a code APITube does not carry — a real coverage gap. Note also that the United Kingdom is gb, not uk.

Russian and Ukrainian are absent

language.code=ru and =uk return 400 ER0237; source.country.code=ru returns 400 ER0212. GDELT covers both. There is no substitute and no partial coverage.

Measure your own exposure before committing: run your real query on GDELT and count the "Russian" rows.

The language mix will shift

One GDELT response of 250 articles for query=tesla carried 15 languages: 44% Chinese, 36% English, 5% Spanish. APITube's index holds roughly 168 times more English than Chinese (18 313 566 against 109 095).

Those measure different things — a query result against index composition — so do not subtract them. But the direction is unambiguous: a query GDELT answered mostly in Chinese comes back mostly in English, and both APIs return 200 while it happens.

Before the cutover, compare the language histogram, not just the count.

Other things that will bite

No paging on GDELT. maxrecords returns up to 250 and stops — no offset, no cursor. Queries that matched more were worked around by slicing the date range. On APITube that workaround is unnecessary: use page.

seendate is not published_at. GDELT records when it first saw the article; APITube records publication time. For a late pickup these differ, and nothing reconciles them.

Date formats differ. GDELT writes 20260709T120000Z (ISO 8601 basic); APITube's filters take 2026-07-09T12:00:00Z (extended).

Multi-value filters apply the first 3. A fourth domain or language is dropped with no error.

What you gain

The article text itself — so whatever scrapes url today can be deleted, along with its retries, user-agent handling and failure modes. Plus named entities with Wikidata links and per-entity sentiment, sentiment on headline and body separately, IPTC MediaTopics alongside topic and industry axes, publisher political bias and Open Page Rank, readability scores, cross-publisher deduplication via is_duplicate and story.id, and export to eight formats.

Next steps