What it does
GET asks a server for a representation of a resource. It's the workhorse of HTTP — every time you load a webpage, fetch an API record, or click a link, you're almost certainly issuing a GET. Nothing about a GET request is supposed to change server state; you're reading, not writing.
That "read-only" contract is what makes GET special. Browsers prefetch GET requests speculatively. Search engine crawlers hammer them without asking permission. CDNs cache them by default. None of that is safe to do with a method that might mutate data — which is exactly why GET's semantics are guarded so carefully by the spec.
Semantics
| Property | Value | Why it matters |
|---|---|---|
| Safe | Yes | The client isn't requesting a state change — a crawler or prefetcher can issue GETs freely |
| Idempotent | Yes | Ten identical GETs have the same effect as one |
| Cacheable | Yes | Responses are cacheable by default unless headers say otherwise |
| Request body | No (technically allowed, universally ignored) | Query parameters go in the URL, not a body |
| Response body | Yes | The whole point of the request |
Syntax & example request
GET /users/42 HTTP/1.1
Host: api.example.com
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: max-age=60
ETag: "v3"
{"id": 42, "name": "Sainesh"}
curl example
curl https://api.example.com/users/42
Common status codes returned
- 200 OK — resource found, here's the representation → see 200
- 304 Not Modified — conditional GET, cached copy is still valid → see 304
- 404 Not Found — no such resource → see 404
GET vs POST vs QUERY
| GET | POST | QUERY | |
|---|---|---|---|
| Safe | Yes | No | Yes |
| Idempotent | Yes | No | Yes |
| Request body | No (ignored) | Yes | Yes |
| Params live in | URL | Body | Body |
| Cacheable | Yes | No (by default) | Yes |
GET's one real weakness: everything has to fit in the URL. Once your filters get complex (nested objects, arrays, long lists), the URL either becomes unreadable or blows past a proxy's length limit — which is the exact gap the new QUERY method was designed to close.
Real-world usage
- Fetching pages, images, static assets — the default browser navigation verb
- REST API reads:
GET /orders/123,GET /orders?status=shipped - Health checks and polling endpoints
- Conditional requests with
If-None-Match/If-Modified-Sinceto avoid re-downloading unchanged data
Security considerations
Because GET puts everything in the URL, anything sensitive in a query string ends up in browser history, proxy logs, server access logs, and the Referer header of any link a user clicks from that page. Never put passwords, tokens, or PII in a GET query string — use POST (or QUERY) with a body instead, over HTTPS.
GET's safety guarantee is a convention, not an enforced rule — nothing stops a server from mutating state on a GET. Doing so breaks browser prefetching, crawler behavior, and caching in ways that cause real bugs (a crawler "deleting" every item in an admin panel by following GET /delete/:id links is a classic horror story).
FAQ
Can GET have a request body?
Technically HTTP doesn't forbid it, but in practice: don't. Many clients, proxies, and servers strip or ignore GET bodies entirely, so behavior is unpredictable. If you need a body with safe/idempotent semantics, use QUERY instead — it's the standardized answer to this exact problem.
Why is GET cacheable by default?
Because it's safe — a GET is defined as not changing anything, so serving a slightly stale cached copy is (usually) harmless. Cache-Control, ETag, and Last-Modified let you tune exactly how stale is acceptable.
Is GET always idempotent in practice?
By spec, yes. In practice, a GET that triggers a side effect (like incrementing a view counter) is still spec-safe as far as HTTP is concerned — the side effect isn't something the client requested, and most implementations treat it as acceptable. But GETs that create, delete, or meaningfully mutate data are a misuse of the method.
Fun fact
GET is so foundational that the HTTP/0.9 protocol (1991) supported only GET — no headers, no status codes, no other methods. You sent GET /page.html and got back raw HTML. Everything else — POST, headers, status codes — was added in HTTP/1.0 a few years later.