๐ก We check shibe.online (and 89 other public dev APIs) with a plain GET every hour โ see the live status page. It was already failing when we added it on September 19, 2026, and has not answered successfully since.
The classic first-fetch starter project โ "get a random dog picture and put it on the page" โ very often meant this URL:
curl 'https://shibe.online/api/shibes?count=3'
# curl: (60) SSL certificate problem: unable to get local issuer certificate
That's today's response, checked live on September 19, 2026 โ and it's the good failure mode, because at least it's an error message. In a browser, fetch() rejects with a bare TypeError: Failed to fetch before any HTTP status exists: no res.ok to branch on, no body, nothing. Every fallback path that checks a status code is skipped, and the app just breaks. We probed every escape hatch: with certificate verification disabled (curl -k) the request dies mid-stream with an HTTP/2 PROTOCOL_ERROR; plain http:// gets an empty reply; from Cloudflare's edge, where our monitor runs, it answers HTTP 520. And cdn.shibe.online โ the host every image URL it ever returned points at โ fails the same way, so shibe URLs your app saved to favorites, a database, or localStorage are dead too.
Credit where due: shibe.online was a lovely on-ramp. Keyless, CORS-open, one GET returning a bare JSON array of picture URLs โ shibas at /api/shibes, plus /api/cats and /api/birds, with a count= parameter for batches. Countless people's first successful API call showed them a dog. That's worth replacing properly.
If what you need is actual shiba photographs, dog.ceo is alive (checked the same day): https://dog.ceo/api/breed/shiba/images/random. Note the shape differs โ it returns {"message":"โฆjpg","status":"success"}, an object rather than shibe's bare array, so it's a one-line client change (our dog.ceo guide covers its endpoints and error quirks). For cats, cataas.com serves an image directly at /cat (200 today) and TheCatAPI's /v1/images/search answers keyless. For birds we know of no keyless drop-in that's currently up. All of these are third-party services run on someone else's goodwill โ which is exactly how this page came to exist. Point production at whichever you like; point development, tests, and CI at something you control:
Two curls. First a project, then a custom route at shibe's exact path returning shibe's exact shape โ a bare JSON array of image URLs:
curl -X POST https://mockbird.mockbird.workers.dev/api/projects \
-H 'content-type: application/json' -d '{"name":"shibe-pics"}'
# โ {"id":"YOUR_ID","adminKey":"YOUR_ADMIN_KEY", โฆ} โ save both
curl -X POST https://mockbird.mockbird.workers.dev/api/projects/YOUR_ID/routes \
-H 'x-admin-key: YOUR_ADMIN_KEY' -H 'content-type: application/json' \
-d '{"method":"GET","path":"/api/shibes","contentType":"application/json",
"body":"[\"https://mockbird.mockbird.workers.dev/m/YOUR_ID/img/500x400?text=shibe&seed=shibe-{{rand}}\",\"https://mockbird.mockbird.workers.dev/m/YOUR_ID/img/500x400?text=shibe&seed=shibe-{{rand}}\",\"https://mockbird.mockbird.workers.dev/m/YOUR_ID/img/500x400?text=shibe&seed=shibe-{{rand}}\",\"https://mockbird.mockbird.workers.dev/m/YOUR_ID/img/500x400?text=shibe&seed=shibe-{{rand}}\",\"https://mockbird.mockbird.workers.dev/m/YOUR_ID/img/500x400?text=shibe&seed=shibe-{{rand}}\",\"https://mockbird.mockbird.workers.dev/m/YOUR_ID/img/500x400?text=shibe&seed=shibe-{{rand}}\",\"https://mockbird.mockbird.workers.dev/m/YOUR_ID/img/500x400?text=shibe&seed=shibe-{{rand}}\",\"https://mockbird.mockbird.workers.dev/m/YOUR_ID/img/500x400?text=shibe&seed=shibe-{{rand}}\",\"https://mockbird.mockbird.workers.dev/m/YOUR_ID/img/500x400?text=shibe&seed=shibe-{{rand}}\",\"https://mockbird.mockbird.workers.dev/m/YOUR_ID/img/500x400?text=shibe&seed=shibe-{{rand}}\"]"}'
curl "https://mockbird.mockbird.workers.dev/m/YOUR_ID/api/shibes"
# โ ["https://โฆ/img/500x400?text=shibe&seed=shibe-185125", โฆ] 10 URLs, JSON array of strings
Each {{rand}} resolves independently on every request, so โ like the real shibe.online โ every call draws a fresh random batch, and every URL in it renders a different picture. The pictures themselves are Mockbird's own deterministic placeholder images (SVG, seeded palette): the same URL renders the same image forever, they load from the same host as your mock, and they can't 404 out from under a saved favorite. They are placeholders, not photographs โ for building UI, seeding tests, and CI, that's a feature: no third-party host in the loop.
Shibe's other two endpoints are the same route with a different seed prefix:
curl -X POST https://mockbird.mockbird.workers.dev/api/projects/YOUR_ID/routes \
-H 'x-admin-key: YOUR_ADMIN_KEY' -H 'content-type: application/json' \
-d '{"method":"GET","path":"/api/cats","contentType":"application/json",
"body":"[\"https://mockbird.mockbird.workers.dev/m/YOUR_ID/img/500x400?text=cat&seed=cat-{{rand}}\",\"https://mockbird.mockbird.workers.dev/m/YOUR_ID/img/500x400?text=cat&seed=cat-{{rand}}\",\"https://mockbird.mockbird.workers.dev/m/YOUR_ID/img/500x400?text=cat&seed=cat-{{rand}}\"]"}'
# repeat with path /api/birds and text=bird for the full trio
curl "https://mockbird.mockbird.workers.dev/m/YOUR_ID/api/cats"
One honest limitation: a fixture route has a fixed length, so ?count= is ignored โ the route above always returns what you put in it. Take fewer client-side with .slice(0, n), or make the route as long as your maximum. If you want a picture collection your app can write to โ favorites, adoption lists, galleries with real limit=, sortBy=random, filters and POSTs โ use records with image fields instead; section 1 of the dog.ceo guide is that recipe.
Shibe died at the TLS layer, and there's an uncomfortable truth in that: no HTTP response can simulate it. Your fetch() rejects โ same as being offline โ so that branch of your code is exercised by pointing at a port nobody's listening on, not by any status code. What you can rehearse deterministically is every HTTP-layer way a picture API dies, on the same route you just built:
# what our monitor sees from the edge โ a 520, on demand:
curl "https://mockbird.mockbird.workers.dev/m/YOUR_ID/api/shibes?mock_status=520"
# two failures then success โ exercise your retry logic:
curl "https://mockbird.mockbird.workers.dev/m/YOUR_ID/api/shibes?mock_seq=520,520,200&mock_seq_key=retry-test"
# the slow-death hang, for timeout testing:
curl --max-time 2 "https://mockbird.mockbird.workers.dev/m/YOUR_ID/api/shibes?mock_delay=5000"
# โ curl exit 28
More recipes in testing loading & error states.
| Shibe.online | Your Mockbird project |
|---|---|
GET /api/shibes โ bare array of URL strings | custom route, byte-identical shape (section 1) |
| random pictures every call | {{rand}} per URL, fresh every request |
/api/cats, /api/birds | same route, different seed (section 2) |
?count=1-100 | fixed route length โ .slice(), or records with real limit= |
| real photos | deterministic placeholders (photos: dog.ceo, cataas โ see above) |
| images on cdn.shibe.online (down with it) | images served by the same host as the API |
| read-only | full CRUD, request inspector, snapshots, error drills |
The two curls in section 1, or zero setup at all:
curl https://mockbird.mockbird.workers.dev/m/demo/products?limit=3
Facts checked live on September 19, 2026: https://shibe.online/api/shibes?count=3 fails TLS verification in default-config curl (unable to get local issuer certificate โ the server presents a certificate with valid dates but without a verifiable chain); curl -k to the same URL dies with HTTP/2 stream โฆ PROTOCOL_ERROR; http://shibe.online/ returns Empty reply from server; https://cdn.shibe.online/ fails identically; our hourly monitor's edge checks record HTTP 520. Same day: dog.ceo answered 200 on /api/breed/shiba/images/random and /api/breeds/list/all; cataas.com/cat answered 200 image/jpeg; TheCatAPI /v1/images/search answered keyless. Every Mockbird command on this page was run against a live project before publishing.
Full API reference in the docs. More guides: dog.ceo guide ยท placeholder image API ยท PNG placeholders ยท mock any endpoint. Create your API โ