Georgia's public documents, for your code
A read-only JSON API over the Official Code of Georgia Annotated, General Assembly bills, and Supreme Court of Georgia opinions. There is no key, no account, and no sign-up. Point a script at it and start reading.
Base URL
https://api.georgiacommons.org/api- Public and read-only. Every route below is a
GET, apart from the twoPOSTroutes that check a quotation. There is nothing to authenticate and no key to request. - One grammar across collections. Each collection answers the same shape at
https://api.georgiacommons.org/api/{collection}, where the collection iscode,bills, oropinions. Learn one and you know all three. - The same data everywhere. This API, the pages on georgiacommons.org, and the MCP server read the same stores. Nothing is a copy that can fall behind.
- Ask the API what it holds. Coverage is never a claim typed into a page.
/{collection}/metareports what is actually loaded, computed when you ask: https://api.georgiacommons.org/api/code/meta.
Your first request
Statutes are addressed by citation. Ask for the Georgia security deposit statute, O.C.G.A. 44-7-34:
curl "https://api.georgiacommons.org/api/code/sections/44-7-34"The response carries the section's identity, its status, what it is current through, the addresses of its page and its Markdown twin, and every stored version with its text. Abbreviated:
{
"citation": "44-7-34",
"catchline": "Return of security deposit; grounds for retention of part; ...",
"title": 44,
"chapter": "7",
"status": "active",
"current_through": "Including Acts of the 2025 Regular Session of the General Assembly",
"canonical_url": "https://georgiacommons.org/code/44-7-34",
"md_url": "https://georgiacommons.org/code/44-7-34.md",
"versions": [ ... ],
"previous": { ... },
"next": { ... }
}A bill and an opinion answer the same way at their own addresses: /api/bills/bills/2025-2026/hb136 and /api/opinions/cases/S26A0060. The full list is on the endpoints page.
Identifiers are the ones you already use
There are no internal ids to look up first. A document is addressed by the name it has in the world: a citation, a session and a bill number, a docket number.
| Collection | Address | Example |
|---|---|---|
| Code | /api/code/sections/{citation} | 44-7-34 |
| Constitutions | /api/code/constitution/{ga|us}/{slug} | art-i-sec-i-para-i, amend-xiv |
| Bills | /api/bills/bills/{session}/{number} | 2025-2026/hb136 |
| Opinions | /api/opinions/cases/{docket} | S26A0060 |
Written forms that are not the canonical one still resolve. /{collection}/resolve?q= reports where a reference leads, and it accepts what people actually write: HB 136, H.B. 136, House Bill 136, a reporter citation such as 883 S.E.2d 746, a lower-case docket, or an upstream LegiScan or CourtListener id. Upstream ids also redirect permanently from the document routes, so an old link keeps working.
A 404 is written to be useful: it names the nearest sections, the chapter above, or the adjacent bill numbers, so a script that guessed wrong can correct itself without a second search.
Markdown and plain text, not just JSON
Every document has three forms. JSON is for programs that want fields. Markdown is for anything that wants to read the document, a language model included. Plain text is the source text alone, with nothing added.
- JSON: https://api.georgiacommons.org/api/code/sections/44-7-34
- Markdown: https://georgiacommons.org/code/44-7-34.md
- Plain text: https://georgiacommons.org/code/44-7-34/text
The Markdown and plain-text forms are served from the site host, not the API host: add .md or /text to any page address, or send Accept: text/markdown to the page itself. Each twin opens with frontmatter naming the citation, the official source, the date, the corpus version, and the license, then the source text. Anything Georgia Commons wrote sits after the source text, under a heading that says so. The Code and the constitutions have nothing after the source text at all.
The API also serves Markdown directly, for callers that would rather stay on one host: /{collection}/index/markdown, /about/markdown, /search/markdown, and a /markdown route on every document.
Search
Each collection has a full-text index over the document text, its headings, and, for bills and opinions, the labeled summaries. ?q= takes words; &in=headings restricts the match to headings and titles.
curl "https://api.georgiacommons.org/api/code/search?q=security+deposit&limit=5"The response separates direct from hits. A bare citation, bill number, or docket among the words is a lookup, and it comes back in direct. hits holds the ranked full-text matches. terms and dropped report which of your words were searched and which were dropped as stopwords, which is usually enough to explain a surprising result.
Search matches the words of the document. Ask in the words the document would use, not the words of the question. A statute about a child says "minor"; an opinion about a car crash says "motor vehicle".
Reading many documents, and checking a quote
/{collection}/items?ids= reads up to 20 documents in one call. The Code also accepts a range, 44-7-30..44-7-35, up to 40 sections. When a request exceeds the cap the response names the remainder in next_ids, so a loop can continue without bookkeeping.
POST /{collection}/verify answers one question: does this quotation appear, word for word, in the stored document? It normalizes the same way the ingestion quote filter does, and no model is involved.
curl -X POST "https://api.georgiacommons.org/api/code/verify" \
-H "Content-Type: application/json" \
-d '{"id": "44-7-34", "quote": "security deposit"}'{
"id": "44-7-34",
"found": true,
"occurrences": 7,
"in": "text",
"chars": 16,
"canonical_url": "https://georgiacommons.org/code/44-7-34",
"md_url": "https://georgiacommons.org/code/44-7-34.md",
"corpus_version": "2025-supplement-3590ef6b4551"
}Quotes shorter than 12 characters are refused with a 422: below that, a match means nothing.
Caching
Read routes carry an ETag and a Cache-Control. Send the tag back in If-None-Match and an unchanged document answers 304 with no body.
curl -s -D - -o /dev/null "https://api.georgiacommons.org/api/code/sections/44-7-34" | grep -i etag
curl -s -o /dev/null -w '%{http_code}\n' \
-H 'If-None-Match: "<the etag you were given>"' \
"https://api.georgiacommons.org/api/code/sections/44-7-34"- Send the tag back exactly. Comparison is string equality, quotes included. Weak tags (
W/), comma-separated lists, and*are not handled. - The tag is deterministic. It is a hash of the response body, so the same corpus always produces the same tag. A tag you stored last week is still valid if nothing changed.
- Freshness by kind. Documents are
max-age=86400with a week of stale-while-revalidate; lists and search aremax-age=3600with a day.verifyisno-store. - No `If-Modified-Since`. Date-based revalidation is not implemented. Use the ETag.
- Ask with a GET.
curl -Isends a HEAD, and the ETag does not come back on one. Usecurl -s -D - -o /dev/nullas above.
Rate limits and errors
Reads are limited to 120 requests a minute and 2,000 an hour per caller, shared across all three collections. Over the limit is a 429 with a Retry-After header saying how long to wait. Honor it; there is no penalty for backing off and no benefit to retrying sooner.
If you need more than that for a one-off analysis, take the bulk download instead of walking the API, and write to the address on the about page if you need something the bundle does not cover.
| Status | What it means |
|---|---|
200 | Here it is. |
301 or 308 | You used an upstream id or a form with a canonical address. Follow the redirect; it is permanent. |
304 | Your If-None-Match tag still matches. Nothing changed. |
404 | No such document. The body names the nearest ones and the unit above. |
422 | The request was understood but not usable, for instance a quote under 12 characters. |
429 | Over the rate limit. Wait for Retry-After seconds. |
Two routes are not part of this API: the streaming question answering behind the chat panels on bill and opinion pages. They hold a server-side key, they are the site's own, and there is no way to obtain that key. Everything else on this page is open to anyone.
Bulk download
The whole Code, with both constitutions, is published as a downloadable bundle. https://api.georgiacommons.org/api/code/bulk returns a JSON listing of the release page, the checksum file, and each asset with its address.
curl "https://api.georgiacommons.org/api/code/bulk"- Five files. The Code, the Georgia constitution, and the United States constitution as gzipped JSON Lines, a manifest, and
SHA256SUMSover the four. Verify the checksums before you load anything. - Reproducible. The gzip streams carry no timestamp, so the same corpus publishes the same bytes and a checksum is comparable across downloads.
- CC0. The Code corpus is public law with no rights reserved.
- Bills and opinions have no bundle yet. Enumerate them through
/{collection}/sitemap-entriesand read them with/items?ids=.
For AI agents
Georgia Commons is built to be read by assistants and agents as much as by people. If you are writing one, or pointing one here, these are the addresses that matter:
- MCP server. One connector for the whole site, streamable HTTP, no key: https://mcp.georgiacommons.org/mcp. The MCP page has the per-client steps.
- Site map for machines. https://georgiacommons.org/index.md, one block per collection with every address it answers.
- llms.txt. https://georgiacommons.org/llms.txt, in the llms.txt convention, with worked examples of every address form.
- For assistants. https://georgiacommons.org/for-assistants, the one-screen reference for an agent's operator: the URL grammar and one worked example per collection.
- OpenAPI schema. https://api.georgiacommons.org/openapi.json describes every route this API serves. There is no hosted Swagger or ReDoc page; paste the schema into the viewer you prefer. The two chat routes appear in it and answer 403 without the site's key.
Crawling is welcome, including for training. robots.txt says so by name on both hosts, and it carries Content-Signal: search=yes, ai-input=yes, ai-train=yes.