Skip to content

Migrating from Perigon

Replace /v1/all with /v1/news/everything. Most parameters have a direct counterpart, which is exactly why the three that differ silently are the ones to fix first: paging is 0-indexed on Perigon and 1-indexed here, unquoted adjacent terms mean OR on Perigon and AND here, and the two sentiment models are not the same measurement.

Why teams switch

PerigonAPITube
Articles per request100250
SentimentThree probabilities per articleSigned score for title, body, overall, and per entity
Category taxonomy15 fixed namesIPTC MediaTopics — thousands of codes, plus topics and industries
Publisher metadataSource name and domainDomain, country, political bias, Open Page Rank
Article textFull contentFull body and body_html, plus an extractive summary

Your first request

bash
# Perigon — page 0 is the first page
curl "https://api.goperigon.com/v1/all?q=tesla&language=en&page=0&size=100" \
  -H "x-api-key: YOUR_KEY"
bash
# APITube — page 1 is the first page
curl "https://api.apitube.io/v1/news/everything?title=tesla&language.code=en&page=1&per_page=100" \
  -H "X-API-Key: YOUR_API_KEY"

The three traps

1. Paging is off by one, and APITube does not error. Perigon pages start at 0; APITube pages start at 1 and treat page=0 as page=1. Forward the number unchanged and your first two pages return identical articles with a 200 — verified: page=0 and page=1 return the same three ids, page=2 is the first that differs. Always send perigon_page + 1.

2. The default operator is inverted. Perigon reads q=Elon Musk as Elon OR Musk; APITube reads title=Elon Musk as Elon AND Musk. On the same corpus that is 33 123 articles versus 20 409 — a 38% drop with no error. Translate to query=title:(Elon OR Musk). Quoted phrases ("Elon Musk") mean the same thing on both and port unchanged.

3. Sentiment does not convert exactly. Perigon returns {positive, negative, neutral} summing to 1. APITube returns a signed score per axis. Use score ≈ positive − negative — so positiveSentimentFrom=0.6 becomes sentiment.overall.score.min=0.2 — then re-tune against real results. neutralSentimentFrom and neutralSentimentTo have no equivalent at all.

Parameter mapping

PerigonAPITubeNote
qtitle or queryHeadlines only, and the default operator inverts — see above
titletitleLossless
content, desc, urlNo body, description or URL search
categorycategory.id13 of 15 names map to IPTC codes, max 3
excludeCategoryignore.category.id
topictopic.idDifferent taxonomies — verify each value
sourcesource.domainMax 3, exact domain
excludeSourceignore.source.domain
sourceCountrysource.country.code
companyNameorganization.nameStrip legal suffixes — Apple Inc returns 400
personNameperson.nameResolves against an index, not free text
companyDomainDifferent meaning — the company's site, not the publisher
companyId, personWikidataIdAPITube entity ids are its own
bylineauthor.nameReal journalist names only; desk names return 400
languagelanguage.codeNo ru, no uk
from / topublished_at.start / .end
addDateFrom, refreshDateFromPublication date only
positiveSentimentFrom / Tosentiment.overall.score.min / .maxApproximate
negativeSentimentFrom / Tosentiment.overall.score.max / .minApproximate, sign inverts
neutralSentimentFrom / ToNo neutral axis
showReprints=falseis_duplicate=0Reprint grouping is lost
medium=Videohas_video=1"Has a video" ≠ "is a video"
lat / lon / maxDistance/v1/news/local lat / lng / radiusA different endpoint
pagepage+1
sizeper_page250 max (400 ER0171 above)
sortBy=datesort.by=published_at
sortBy=relevanceReturns 500 with a search term; use sort.by=source.rank.opr

The full table of every Articles parameter is in the migration kit.

What does not carry over

Body-text search. content=, desc= and url= have no equivalent, and APITube ignores unknown parameters silently — forwarding them returns the whole index with a 200. Search headlines and filter the returned body locally.

Wildcards. q=immuni* fails the same way: title=immuni* is accepted and returns the entire index. Expand into query=title:(immunity OR immunization OR immunology).

Relevance boosting. ^2 has no equivalent. Sent verbatim it becomes a literal search term.

Google Content Categories. taxonomy=/Finance/Banking and prefixTaxonomy use a hierarchy with no mechanical mapping to IPTC. Pick an IPTC code by hand.

Clustering endpoints. /v1/stories/all has no counterpart — every article carries story.id for client-side grouping, but there is no cluster endpoint and no clusterId filter. /v1/vector/news/all has none either: APITube has no embeddings. /v1/summarize is largely covered by the summary array already on every article.

Russian and Ukrainian. language.code=ru and uk return 400 ER0237. There is no substitute.

What you gain

250 articles per request instead of 100, sentiment on the headline and the body separately plus a score towards each extracted entity, IPTC MediaTopics alongside topics and industries, publisher political bias and Open Page Rank, readability scores, and export to eight formats.

Next steps