Personality — entry 007 of 17
icanhazdadjoke
icanhazdadjoke serves a large library of dad jokes as JSON (or plain text/Slack markup via content negotiation), fully keyless. A live GET against the real host, icanhazdadjoke.com, with an Accept: application/json header returned a genuine random joke while carrying Access-Control-Allow-Origin: *, resolving cors unknown -> yes; no key was required, reconfirming auth None. A fully free, keyless joke API — pricingModel free.
icanhazdadjoke serves a large library of dad jokes as JSON, plain text, an image, or a Slack-formatted message, chosen entirely through content negotiation on the Accept header — one URL per joke, no query parameters for format. Its own docs (icanhazdadjoke.com/api) state plainly: "No authentication is required to use the icanhazdadjoke.com API."
GreatAPIs Score
Auth quickstart
- No account or key is needed, but the request must carry `Accept: application/json` — its own docs warn that a bare curl request with no Accept header gets `text/plain` by default, confirmed live: the same GET without the header returned plain joke text, not JSON.
- The docs also ask (not require) a descriptive `User-Agent` identifying your app or library, so they can distinguish real integrations from abusive traffic.
Your key is stored only in this browser (localStorage) and sent directly to the API — never to greatapis.
Fetch a random dad joke as JSON
GEThttps://icanhazdadjoke.com/
Accept: application/json
{"id":"TC5TCt49prc","joke":"Why did the A go to the bathroom and come out as an E? Because he had a vowel movement.","status":200}TC5TCt49prc is whichever joke the API's random selector returned on this run's live probe, not a curated pick — captured directly and byte-for-byte, no truncation.
Developer reference
https://icanhazdadjoke.com100 requests per IP per fixed ~60s window, anchored at the window's first request (measured live via x-ratelimit-limit/-remaining/-reset response headers; not documented in the API's own docs)
Gotchas & limits
- Omitting `Accept: application/json` does not error — it silently serves `text/plain` (or HTML for a browser's default Accept) instead of JSON, confirmed live against the same URL with no header.
- GET /j/{id} fetches one specific joke by id; a nonexistent id still returns a clean JSON body at HTTP 200, with 404 appearing only in the body's own `status` field — confirmed live on two separate bad ids, e.g. /j/doesnotexist12345 returned HTTP 200 with {"message":"Joke with id \"doesnotexist12345\" not found","status":404}. Check the body, not the status code — a genuinely bogus path outside /j/ does return a real HTTP 404, so the host is not masking statuses generally.
- GET /search accepts `term`, `page`, and `limit` (default 20, max 30 per the docs) query parameters and wraps matches in a paginated shape (`current_page`/`next_page`/`results`/`total_jokes`) that differs entirely from the single-joke shape — confirmed live against /search?term=cat&limit=1.
- The API is rate-limited per IP to a fixed ~60-second window, anchored at the first request of the window rather than rolling forward with each call: live response headers carried `x-ratelimit-limit: 100` and a decrementing `x-ratelimit-remaining`, while `x-ratelimit-reset` stayed pinned to one value for the whole window — across four requests 8 seconds apart it held at 1785105280 while the distance to it shrank 61s, 53s, 45s, 37s. Once it elapsed, the next request opened a fresh window with `remaining` back at 99. The anchor is not a wall-clock minute boundary either (1785105280 % 60 == 40). None of this is written down anywhere in the API's own docs — the headers are the only source.
- GET /j/{id}.png renders the same joke as a shareable image rather than JSON — useful for embedding, not for programmatic consumption.