Read one law's provisions
https://api.granska.cloud/v1/laws/:jurisdiction/:workReads one law's provisions, each with the pinpoint a profile must cite it by.
Why you need this before you can create a profile
Knowing that the law you want is 1949:381 is not enough.
POST /v1/profiles takes each reviewer's legal reference as three
fields — jurisdiction, work and pinpoint — and the pinpoint must be spelled exactly as this
library addresses the provision. Nothing you can build yourself will match it.
This endpoint is where you get it. Take a pinpoint from the response and send it back verbatim.
There is one shortcut, and it is the United States Code's alone. That catalogue is section-level, so
a GET /v1/laws hit which names a single section already carries that
section's pinpoint — cite it straight from the hit, without reading the whole title. Search
Sweden, Norway, Denmark or the CFR and the hit is a whole law, pinpoint is null, and this
endpoint is the only place the spelling of its provisions exists.
curl https://api.granska.cloud/v1/laws/SE/1949:381 \
-H "Authorization: Bearer $TOKEN"{
"jurisdiction": "SE",
"work": "1949:381",
"name": "Föräldrabalk (1949:381)",
"authorityType": "STATUTE",
"isRepealed": false,
"repealedAt": null,
"publishedYear": 1949,
"ingested": false,
"provisions": [
{
"pinpoint": "kap_6_par_2a§",
"label": "6 kap. 2 a §",
"chapter": "6 kap.",
"part": null,
"heading": "Om vårdnad, boende och umgänge",
"parts": [
{
"label": null,
"text": "Vid alla frågor som rör vårdnad, boende och umgänge ska barnets bästa vara avgörande…"
}
],
"isRepealNotice": false
}
],
"nextCursor": null
}pinpoint and label are not interchangeable
Read this once and the rest of the endpoint is easy.
pinpoint—"kap_6_par_2a§". The provision's address. This is what you send us.label—"6 kap. 2 a §". The same provision as a lawyer writes it. This is what you show a person.
Nothing translates between them. A reference is matched against the pinpoint by exact equality and is never parsed, so a label sent as a pinpoint is stored without any complaint and then resolves to nothing on every analysis that runs afterwards. The profile looks complete, the law exists, the reviewer appears to be configured, and no reviewer ever reads the provision.
That is not hypothetical: it is what our own published examples told people to do until it was
found. Copy the pinpoint field. Never build one by hand, and never assemble work and pinpoint
into a single string.
| Parameter | Description |
|---|---|
jurisdictionstring·path·required | Which legal order the law belongs to, as a code such as SE. Only a legal order with an integrated law source resolves — SE, NO, DK and US_FED resolve today; the refusal names the ones that do. |
workstring·path·required | Which law, exactly as GET /v1/laws spells it. Opaque — never parsed, and never assembled with the pinpoint into one string. |
{
"provisions": [
{
"pinpoint": "kap_6_par_2a§",
"label": "6 kap. 2 a §",
"chapter": "6 kap.",
"parts": [{ "label": null, "text": "Vid alla frågor som rör vårdnad…" }],
"isRepealNotice": false
}
]
}
What a call costs, and when
The first request for a law this library does not hold fetches it from the national source, parses it and stores it. That takes two to five seconds and spends one tick of your organisation's hourly configuration-write floor — the same abuse floor that bounds profile writes. It spends no analysis quota.
Every request for that law during the next 24 hours is answered from storage. It is fast, and it costs nothing at all. So the second caller in your organisation to ask for a law pays nothing, and neither do you when you ask again.
The ingested field tells you which of the two you got: true means this call fetched the law,
false means it was already held. The X-RateLimit-* headers are on both answers, so you can
read where your floor stands without spending anything to find out.
If the floor is spent you get a 429 — and nothing is fetched, so you are not charged for a law you
did not receive.
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1786838400
What comes back
provisions is a fixed list of fields, not the stored record, so nothing we add to our database
tomorrow can appear in your response.
Each provision carries its pinpoint, its label, the chapter and heading it sits under, and
its text as ordered parts. A part has a label when the source prints one for it — "(f)" on a
lettered item — and null when it does not. A part is not separately citable: the pinpoint
addresses the whole provision.
chapter is always the level the provision's own address names — 6 kap. for a Swedish provision,
Kapittel 5. Stønad ved helsetjenester for Norwegian § 5-15. Some Norwegian acts print an outer
division above that chapter, a del, and it comes back as part: "Del IV Ytelser ved sykdom mv.".
It is null for every act that prints none, which is every Swedish one. Cite by the chapter —
a del is not part of how a provision is addressed. Note that part and parts are unrelated:
parts is the provision's own text.
isRepealNotice marks a provision that records a repeal rather than stating a rule. Citing one is
almost never what you want.
isRepealed on the law itself says the whole law has been repealed. It is still returned, and still
citable — whether that is correct is a judgment about your analysis, not one this endpoint makes for
you.
work is opaque. Never parse it, split it or pattern-match on it: its shape differs per jurisdiction
and is not part of the contract.
attribution carries the source line that jurisdiction's licence requires be shown wherever its text
is displayed, and is null where no such duty exists. Norwegian provisions come from Stiftelsen
Lovdata under NLOD 2.0 and always carry one; Swedish provisions never do. Show it wherever you show
the provision text, and read it from the response rather than hardcoding the sentence. The same
field and the same duty appear on GET /v1/laws.
Long laws come back a page at a time
Most laws fit one response and you will never see this. A few do not: a work in the U.S. Code is a whole title, and 42 U.S.C. is 8 590 sections — far more than any response can carry.
So provisions is one page of the law, and nextCursor tells you whether there is another.
When it is null you have the whole law. When it is a string, send it straight back as cursor to
get the next page, and keep going until it is null.
nextCursor is on every response, including the short ones, so you can write the loop once and it
works for every jurisdiction. Every Swedish, Norwegian and Danish law we hold arrives in a single
page.
Two things worth knowing:
- Paging is free. Only the first request for a law can fetch it, so a title costs one tick of your write floor however many pages you read.
- The cursor is opaque and short-lived. Send it back exactly as you received it; never build one
or store one for later. If the law is re-published between two of your pages we refuse the cursor
with a
400rather than hand you a page that quietly skips provisions — ask for the work again without a cursor and page it from the start.
| Parameter | Description |
|---|---|
cursorstring·query | The nextCursor a previous response carried, to read the page after it. Omit for the first page. Opaque — send it back exactly as received, never build one. Paging costs nothing: only the first call can fetch the law. |
# Page through a whole title
cursor=""
while :; do
page=$(curl -s -G "$API/v1/laws/US_FED/42%20U.S.C." \
-H "Authorization: Bearer $TOKEN" \
${cursor:+--data-urlencode "cursor=$cursor"})
echo "$page" | jq -r '.provisions[].pinpoint'
cursor=$(echo "$page" | jq -r '.nextCursor // empty')
[ -z "$cursor" ] && break
done
Errors
A 404 means one of two things and the message says which: there is no such law, or this
jurisdiction's laws arrive in bulk archives and asking cannot add one. The second is not a
temporary condition — retrying will not help, and the law will appear, or not, at the next bulk
refresh.
Neither can happen for a law GET /v1/laws reported as held. A law we
hold is always served, including one we have no way to refresh; the 24-hour window decides whether we
go and look again, not whether the text is ours to give you.
A 400 means one of two things. Either the jurisdiction is not one this API carries — SE,
NO, DK and US_FED resolve today. Ask GET /v1/laws first if you are
not sure a law is here.
Or the cursor you sent is not one we issued, or no longer addresses the law because it was
re-published between your two calls. Ask for the work again without a cursor and page it from the
start.
A 503 means the source was unreachable, or that we could not confirm whether the law has been
repealed. In that second case nothing was stored — we would rather answer nothing than store a
repeal status we guessed. Retry.
| Error | When |
|---|---|
400BAD_REQUEST | The jurisdiction named has no integrated law source and the message names the ones that do; or cursor is not a token this API issued, or the law was re-read between two of your pages so resuming it would skip provisions — in which case ask for the work again without a cursor. |
401UNAUTHORIZED | The Authorization header is missing, is not a readable bearer token, or names no tenant. |
401TOKEN_EXPIRED | The access token was issued by this gateway and has since expired. Not probed: it needs a token older than its own lifetime. |
404NOT_FOUND | There is no such law, or this jurisdiction's corpus arrives in whole bulk archives so asking cannot add it. The message says which of the two. |
429TOO_MANY_REQUESTS | The organisation has spent its hourly configuration-write floor. Only a real ingest ticks it; a law already held costs nothing. This is an abuse floor and not a plan limit, and no analysis quota is consumed. Not probed: reaching it would mean asking for sixty laws we do not hold. |
500INTERNAL_ERROR | An unexpected server-side failure. Not probable from outside — reaching it means something is wrong. |
503SERVICE_UNAVAILABLE | The source is unreachable, or the register that states whether the law is repealed could not be read — in which case nothing is stored rather than a repeal status being guessed. Retry. Not probed: it needs the upstream source to be down. |
The whole journey
GET /v1/laws?jurisdiction=SE&query=…— find the law, and see whether it is held.GET /v1/laws/SE/{work}— read its provisions and copy thepinpointyou want.POST /v1/profiles— create the profile citing it.