alluvDeveloper

Alluv API

HTTP endpoints at api.alluv.dev: the state of the service, and how many Swedish housing cooperatives the corpus covers.

Getting started

Every endpoint answers GET over HTTPS with JSON, and every response is marked Cache-Control: no-store. The base URL is https://api.alluv.dev.

An endpoint that needs a key says so below. With a key in hand, this is the whole of a request:

curl -H "Authorization: Bearer $ALLUV_API_KEY" \
  https://api.alluv.dev/association/latest/counts

Versions

The version is part of the association path. latest is the only version so far.

Authentication

Send your key in either of these headers:

Authorization: Bearer YOUR_KEY
X-API-Key: YOUR_KEY

Treat a key as an opaque string: its length and format differ from key to key. It is shown once when it is issued, so keep it somewhere safe, and keep it out of public repositories. The API sends no CORS headers, so call it from a server rather than from a web page.

A key can carry an expiry date and a list of addresses it may be used from. Outside either, it is refused as invalid_key.

A key you send is always checked, even on an endpoint that does not need one: a bad key gets an error there too, rather than being ignored. The health endpoints are the exception, and never look at a key.

Getting a key

Keys are issued by hand. There is no sign-up. To ask for one, write to cohort-word-9u@icloud.com and say what you intend to build.

Rate limits

A key can carry a limit: a number of requests per window. The window is fixed, so the count starts over when it ends instead of sliding along with your requests. One count covers the key across every API here. The health endpoints do not count.

A response to a request made with a limited key says where you stand:

RateLimit-Policy: "default";q=100;w=60
RateLimit: "default";r=42;t=17
FieldMeaning
qRequests allowed per window
wLength of the window, in seconds
rRequests left in this window
tSeconds until this window ends

Over the limit, you get 429 with the error rate_limited and a Retry-After header in seconds. Requests without a key are not counted against any key.

Association API

Under /association/latest.

GET /association/latest/counts

Needs a key. How many associations the corpus covers.

{
  "corpus": 17331,
  "available": 17309,
  "as_of": "2026-11-04T07:44:04Z"
}
FieldMeaning
corpusAssociations with at least one stored annual report, whether or not it has been approved
availableAssociations with at least one approved annual report. Never more than corpus
as_ofWhen the figures were computed, in UTC. They are normally refreshed every five minutes

The figures shown are an example. If no figures have been read yet, the answer is 503 with the error not_ready, never zero. If they cannot be refreshed, you keep getting the last ones, and as_of shows their age.

GET /association/latest/status

Open. Whether the things people use are working.

{
  "services": [
    {"name": "Uploads", "status": "ok"},
    {"name": "Processing", "status": "ok"},
    {"name": "Publishing", "status": "ok"},
    {"name": "Search", "status": "ok"},
    {"name": "Sign-in", "status": "ok"}
  ]
}

A status is ok, down, or unknown, which means the last reading is more than two minutes old and nothing can be said either way. The list of services may grow, so match on name rather than on position.

GET /association/latest

Open. The commit this API is running.

{"version":"f2b430b0"}

GET /association/latest/health

Open. Whether this API is up, and nothing about anything behind it. It looks at no key and counts against no limit.

{"status":"ok"}

Service API

At the root of the host. It holds no data.

GET /

Open. The commit this API is running.

{"version":"f2b430b0"}

GET /health

Open. Whether this API is up. It looks at no key and counts against no limit.

{"status":"ok"}

Errors

An error from the API is JSON with one field:

{"error":"key_required"}
ErrorMeaning
401
key_required
This endpoint needs a key and the request had none. Send a key.
401
invalid_key
The key is unknown, revoked, expired, or used from an address it does not allow. Check the key; retrying will not help.
429
rate_limited
The key has used its limit for this window. Wait for Retry-After.
503
key_check_unavailable
The key could not be checked just now. This says nothing about the key. Retry after Retry-After.
503
not_ready
The figures have not been read yet. Retry after Retry-After.

A path that is not listed here answers 404, and a method other than GET or HEAD answers 405, neither with a JSON body.