Personality — entry 007 of 17

icanhazdadjoke

Verified Jul 2026

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."

jokeshumordad-jokesfun
AuthenticationNone requiredCall it straight away — no key, no signup.
HTTPSSupportedTraffic is encrypted in transit.
CORSEnabledCallable directly from browser JavaScript.
PricingFreeNo paid tier — free for the documented use case.
FormatsJSONResponses can be requested as JSON.

GreatAPIs Score

Score74out of 100
Authentication25/25No authentication required
Pricing20/20Free to use
Docs0/20No docs or spec available
Formats9/15Single response format
Freshness20/20Verified within 6 months

Embed this badge

Scored 74 on greatapis.com
<a href="https://greatapis.com/api/icanhazdadjoke/"><img src="https://greatapis.com/badge/icanhazdadjoke.svg" alt="Scored 74 on greatapis.com"></a>

Auth quickstart

  1. 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.
  2. The docs also ask (not require) a descriptive `User-Agent` identifying your app or library, so they can distinguish real integrations from abusive traffic.
Stored keyNo key stored

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

200 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

Base URLhttps://icanhazdadjoke.com
Rate limit

100 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.