The Directories API
Read and write one directory from a script. One key, scoped to one directory and to the capabilities you tick, sent as a bearer token.
Getting a key
Generate a key in the console under Settings, choosing the directory it is for and what it may do. The secret is shown once, when you create it — copy it then, because we store only a hash of it and cannot show it again. A key you have lost is revoked and replaced.
A key reaches one directory and nothing else, and it works only while you are still a member of the account that owns it. Leaving the account stops the key on the next request; so does revoking it. A key can only ever do less than you can, never more.
What a key may do
A key carries only the capabilities you ticked when you made it. They are not a hierarchy — writing does not imply reading, and neither implies triggering a check.
- listings:read — Read the directory's listings, one page at a time, and read any single listing.
- listings:write — Create and edit listings, and publish, unpublish or archive them.
- directories:read — Read the directory's own name, description and linkback policy.
- directories:write — Change the directory's details and its linkback policy.
- taxonomy:read — Read the directory's categories, tags and collections.
- actions:trigger — Ask for a linkback check or a Domain Rating refresh. Both cost us an outbound request, so both are rate limited.
Authenticating
Every request carries the key in an Authorization header. There is no sign-in step and no token to exchange first — the key you copied from the console is the bearer token.
Authorization: Bearer YOUR_KEY
The header is the only place we read it. A key sent as a query parameter is refused, because a URL ends up in access logs, Referer headers and browser history.
The endpoints
Every endpoint sits under the directory the key was issued for, and each needs exactly one capability. A key without that capability is refused rather than quietly given less.
Listing your listings
Listings are the busiest resource. The index returns every listing in a directory, in name order, a page at a time.
GET /api/v1/directories/DIRECTORY_ID/listings
Query parameters
- page — Which page to return. Counting starts at 1. A page past the end returns no rows rather than an error, and the envelope still says how many pages there are.
- per_page — How many rows a page holds. The default is 25 and the maximum is 100; anything larger is capped rather than refused.
Example request
curl -X GET \
-H "Authorization: Bearer YOUR_KEY" \
"https://directories.prosvita.io/api/v1/directories/DIRECTORY_ID/listings?page=1&per_page=25"
Example response
{
"data": [
{
"id": "018f7c2a-0000-7000-8000-0000000000c2",
"public_id": "9834hd7",
"slug": "my-cool-cafe",
"name": "My Cool Cafe",
"description": "Third-wave coffee in Kreuzberg.",
"website_url": "https://mycoolcafe.example",
"status": "published",
"verification_status": "verified",
"primary_category": {
"id": "018f7c2a-0000-7000-8000-0000000000a1",
"name": "Cafes",
"path": "food/cafes"
},
"tags": [
"wifi",
"outdoor-seating"
],
"properties": {
"opening_hours": "9-5"
},
"created_at": "2026-08-01T09:14:22Z",
"updated_at": "2026-08-27T11:02:05.418291Z",
"content_updated_at": "2026-08-27T11:02:05Z"
}
],
"meta": {
"page": 1,
"per_page": 25,
"count": 412,
"pages": 17
}
}
Every custom field your directory defines appears on every listing, with a null where it has no value, so the shape does not change from row to row.
What comes back
- 200 — A data object, or a data array with a meta object carrying the page, the page size, the total count and the number of pages.
- 201 — A listing was created, and comes back in the shape the index returns.
- 202 — The work was queued. It happens in the background, so the reply describes what was asked for rather than what it found.
- 401 invalid_token — The key is missing, unreadable or revoked.
- 403 insufficient_scope — The key is valid but does not carry the capability this endpoint needs.
- 404 not_found — The directory is not the one this key was issued for, the record is not in it, or you are no longer a member of the account that owns it. We answer the same way in every case, on purpose.
- 409 conflict — You sent an expected_updated_at that no longer matches. Nothing was written, and the record as it now stands comes back with the refusal.
- 422 invalid — A validation refused the write. errors names every message.
- 422 limit_exceeded — Your plan's limit for this directory is already reached. The body names the limit, what you are using and what you are allowed.
- 422 plan_not_permitted — Your plan does not allow the value you sent. The body names the setting and what is permitted.
- 429 throttled — The same action was asked for again too soon. retry_at names the moment you can ask again.
- 503 unavailable — A Domain Rating fetch could not be asked for at all — the listing has no host, or our own credential is paused. Neither is fixed by sending the same request a second later.
What it does not do
A listing is never deleted — archiving is its only exit, so there is no DELETE. Categories, tags and collections are read-only. Nothing here fills a listing in for you. The console's automatic population is not part of the API, because it writes what a model guessed and an operator is meant to read that before it is saved. The API answers on our own domain, never on a directory's custom domain.