The "random quote generator" is one of the most-built beginner projects on the internet, and a huge share of its tutorials point at https://api.quotable.io/random. If yours just failed with a TLS error, here's why: Quotable's certificate expired on September 10, 2024 and was never renewed (we re-checked the live cert while writing this β notAfter: Sep 10 2024). Every default-config client refuses the handshake:
$ curl https://api.quotable.io/random
curl: (60) SSL certificate problem: certificate has expired
// browser
Uncaught TypeError: Failed to fetch // net::ERR_CERT_DATE_INVALID
The cruel part: the server is still answering behind the broken cert. The data is right there β but no browser, no fetch(), no sane HTTP client will touch it, and telling beginners to disable TLS verification is how you teach terrible habits. After two years, "wait for the renewal" is not a plan.
π‘ Is api.quotable.io still down? We check it (and 37 other classic free/mock APIs) every 30 minutes β see the live status page. Spoiler: the Bored API (boredapi.com), the other half of every "fun beginner API" listicle, is gone too β its domain no longer resolves.
Below: one curl that gives you your own quotes API β same record shape as Quotable, a public-domain starter set included, and a real /random equivalent. No signup, no API key, CORS on, and it's full CRUD, so your app can also save quotes (something Quotable never did).
We host a starter dataset β 50 public-domain quotes (Wilde, Twain, Seneca, Austen, Dickinson, Douglassβ¦) in Quotable's field shape: author, authorSlug, content, tags, length. Pipe it into the importer:
curl -s https://mockbird.mockbird.workers.dev/data/quotes.db.json \
| curl -s -X POST 'https://mockbird.mockbird.workers.dev/api/projects/import?name=quotes' \
--data-binary @-
The response contains your project id and an adminKey (save it β it manages the project, and lets you claim it into an account later). Your API is live immediately:
https://mockbird.mockbird.workers.dev/m/YOUR_ID/quotes
sortBy=random shuffles the collection before pagination, so limit=1 gives you one random record per request:
curl "https://mockbird.mockbird.workers.dev/m/YOUR_ID/quotes?sortBy=random&limit=1"
One honest difference: Quotable's /random returned a bare object; this returns an array of one β destructure it. The classic tutorial app becomes:
const res = await fetch(BASE + "/quotes?sortBy=random&limit=1");
const [quote] = await res.json();
quoteText.textContent = quote.content;
quoteAuthor.textContent = "β " + quote.author;
| Quotable | Your Mockbird API |
|---|---|
/random | /quotes?sortBy=random&limit=1 |
/random?maxLength=60 | /quotes?sortBy=random&limit=1&length_lte=60 (minLength β length_gte) |
/random?tags=wisdom | /quotes?sortBy=random&limit=1&tags_like=wisdom |
/quotes?author=β¦ | /quotes?author=Mark+Twain (exact) or ?author_like=twain |
/quotes?page=2&limit=20 | identical β ?page=2&limit=20 (total in X-Total-Count) |
/quotes/:id | /quotes/7 |
/search/quotes?query=stars | /quotes?q=stars |
/authors, /tags | no built-in equivalent β import an authors collection alongside, or derive client-side with ?select=author&limit=100 |
Quotable was read-only. This is a real CRUD API β your app can save quotes:
curl -X POST "https://mockbird.mockbird.workers.dev/m/YOUR_ID/quotes" \
-H 'content-type: application/json' \
-d '{"author":"You","authorSlug":"you","content":"Ship the thing.","tags":["motivational"],"length":15}'
50 quotes is a seed, not a library β the point is that the collection is yours. Paste a bigger dataset into the dashboard import panel (or pipe any db.json/CSV β up to 1,000 records per collection), POST your favourites over time, or PATCH an attribution you disagree with. You also get GraphQL on the same data, error/delay simulation for teaching loading states, and a db.json export to leave whenever you like.
| Quotable | Self-host Quotable | Your Mockbird project | |
|---|---|---|---|
| Works today | β cert expired Sep 2024 | β (Node + MongoDB) | β hosted, ~60s setup |
| Dataset | ~2,000 curated quotes, real tag taxonomy, author pages | same dataset, yours to run | 50-quote public-domain starter; bring/build your own |
| Random endpoint | β /random (bare object) | β | β ?sortBy=random&limit=1 (array of 1) |
| Writes | β read-only | fork it | β full CRUD |
| API key | none | n/a | none |
| Ops burden | n/a (dead) | server + MongoDB + cert renewals (see above) | none, 10k req/day per project |
The 50 starter quotes are from authors whose works are in the public domain (Aesop to Du Bois), with attributions we double-checked β famous lines that are commonly misattributed online (there are a lot of them) were deliberately left out. Spot an error anyway? It's your copy β PATCH it.
The one-liner in section 1 is the whole setup β or open the dashboard and paste the dataset into the import panel. The same trick works for any dead-or-dying hobby API: we also keep ready-made countries, timezones, and currencies datasets, or host any CSV as an API. (Forismatic, the other classic quote API, is in the same graveyard β see the beginner-API graveyard.)