Random trivia is one of those weird primitives that ends up in more software than you would expect. Daily newsletters that open with a curious fact to lift the rest of the email. Trivia apps that need an endless supply of fresh material so the same player does not see the same card twice in a week. Discord and Slack bots that drop a fact-of-the-day at 9am to nudge a quiet channel back to life. Chatbots that fall back to a friendly aside when the user asks something they cannot answer. Educational platforms that intersperse facts between lessons to keep attention. None of these products are about trivia, but every one of them needs a steady supply of short, well-formed, original facts on tap.
This post walks through why programmatic fun-fact data is its own surprisingly fiddly little problem domain, what a clean fact feed actually looks like, and how to pull random facts, fact-of-the-day, or specific facts by ID through our hosted scraper without writing or maintaining any code yourself.
The Useless Facts API is a generous public good, but building production fact features directly on top of any third-party feed has a familiar set of failure modes. The endpoint can be rate-limited, the JSON shape can shift over time, the source domain can move, and the language coverage can change. Beyond plain availability, the consumer side has its own quirks: facts contain quotation marks and special characters that need careful escaping, repeated random calls can return the same fact twice in a row, and the fact-of-the-day endpoint changes once per UTC day — not once per local day — which surprises users in distant timezones.
Random does not mean uniform: A naive integration that fires “give me one random fact” on every page load will, sooner or later, hand the same fact to the same user twice in a session. A production trivia feature wants either a batch-and-shuffle pattern, a per-user seen-set with deduplication, or both. The data layer is one decision; the “do not show this user the same fact twice” layer is a separate one and worth thinking about up front.
The actor normalises every fact to the same compact shape, so downstream code does not have to branch on which endpoint produced it:
{
"id": "5f048654915ace42c3ab2c246fdc2457",
"text": "US gold coins used to say “In Gold We Trust”.",
"source": "djtech.net",
"source_url": "https://www.djtech.net/humor/shorty_useless_facts.htm",
"language": "en",
"permalink": "https://uselessfacts.jsph.pl/api/v2/facts/5f048654915ace42c3ab2c246fdc2457",
"scraped_at": "2026-05-03T12:39:29.238Z"
}
Every run takes a small JSON input describing what you want. There are four supported actions:
| Action | What you get | Required input |
|---|---|---|
random | N random facts | count (default 1), language |
today | The fact of the day (changes once per UTC day) | language |
byId | One specific fact by ID | id, language |
batch | Multiple specific facts by ID | ids (array), language |
{
"action": "random",
"count": 5,
"language": "en"
}
{
"action": "today",
"language": "de"
}
{
"action": "byId",
"id": "5f048654915ace42c3ab2c246fdc2457",
"language": "en"
}
{
"action": "batch",
"ids": [
"5f048654915ace42c3ab2c246fdc2457",
"b2c7f6cd9240e38dd4d574d2697d74ff"
],
"language": "en"
}
The natural pattern is a small scheduled job — a cron, a serverless function, or a daily Apify run — that requests one fact (or a small batch), stores it in your own database keyed by date, and renders it from there. You get caching, replay, and editorial override for free, and the third-party API only sees one call per day per surface instead of one call per page view. For chatbots and Discord/Slack integrations, fetching a small batch of 50 facts in the morning and serving them locally over the rest of the day is almost always the right shape: it stays well inside everyone’s rate limits and keeps response time inside the bot loop fast.
If you want to build the data plumbing yourself rather than use a managed actor, the part that gets people in trouble is not the first request — it is everything around it. Rate limits, retries on transient failures, IP rotation when a single endpoint pushes back, language-aware caching, deduplication for “random” results, and consistent UTF-8 handling for non-ASCII characters in fact text. For anything where the data layer is incidental to the actual product (a newsletter, a bot, an app), a proxy provider like Oxylabs handles the “do not get blocked” layer, and a managed actor handles the “do not babysit a scraper at 3am” layer.
The actor lives on Apify and is callable from any language that can make an HTTP request: Python, Node, Go, Bash, Zapier, n8n, or directly from the Apify console. You pay only for facts returned, with no monthly minimum and no credit card needed for the first runs.