Delivery API
HTTP reference for authentication, and for fetching lists, single entries, objects, and the schema.
The delivery API is a read-only API that returns genko.me content as JSON. The base URL is https://api.genko.me. You write entries in the dashboard.
Authentication
Send an API key issued in the target workspace in the X-API-KEY header on every endpoint under /v1. See Manage API keys for how to issue one.
curl 'https://api.genko.me/v1/your-workspace/blog' \ -H "X-API-KEY: ${GENKO_API_KEY}"In the examples that follow, replace your-workspace, blog, and the entry ID with your own values. They assume GENKO_API_KEY is already set as an environment variable in the shell you run them in.
Endpoints
| Method | Path | Returns |
|---|---|---|
GET | /v1/{workspace}/{endpoint} | A list for a List API, or the single item for an Object API |
GET | /v1/{workspace}/{endpoint}/{entryId} | One entry from a List API |
GET | /v1/{workspace}/_schema | The API definitions in the workspace |
GET | /v1/{workspace}/_views/{endpoint} | All-time and recent entry counts (API key required) |
POST | /pv/{workspace} | Record a view (no API key, 204) |
GET | /pv/{workspace}/counts | All-time entry and path counts (no API key) |
workspace is the workspace ID (the slug, the same value as /w/{slug} in the dashboard), endpoint is the API's endpoint, and entryId is the id in the delivery response. Unless you set one, the ID is a 12-character alphanumeric string issued by genko.me. You can change it in the editor under Entry settings > URL > Slug, and entries imported from other services keep their original IDs. The actual value is 1 to 64 characters from [A-Za-z0-9_-].
A machine-readable OpenAPI 3.1 definition and a catalog of where the API is documented are published without authentication. See OpenAPI and the API catalog for the URLs and how to load them into tools.
Fetch a list
Send a request to the endpoint of a List API.
curl --get 'https://api.genko.me/v1/your-workspace/blog' \ -H "X-API-KEY: ${GENKO_API_KEY}" \ --data-urlencode 'limit=10' \ --data-urlencode 'orders=-publishedAt'{ "contents": [ { "id": "abcDEF123456", "title": "My first post", "body": "<p>This is an entry delivered from genko.me.</p>", "createdAt": "2026-09-01T00:00:00.000Z", "updatedAt": "2026-09-01T01:00:00.000Z", "publishedAt": "2026-09-01T01:00:00.000Z" } ], "totalCount": 1, "offset": 0, "limit": 10}| Key | Meaning |
|---|---|
contents | The array of entries returned this time |
totalCount | The number of entries that meet the publishing conditions and any search or filter |
offset | The position the results start from |
limit | The maximum number returned per request |
If no entries match, you still get 200 with an empty contents array. The total can differ from the count in the dashboard, which includes drafts.
Fetch a single entry
Add the id from the list to the end of the URL.
curl 'https://api.genko.me/v1/your-workspace/blog/abcDEF123456' \ -H "X-API-KEY: ${GENKO_API_KEY}"The response is a single object, not wrapped in contents.
{ "id": "abcDEF123456", "title": "My first post", "body": "<p>This is an entry delivered from genko.me.</p>", "createdAt": "2026-09-01T00:00:00.000Z", "updatedAt": "2026-09-01T01:00:00.000Z", "publishedAt": "2026-09-01T01:00:00.000Z"}If the entry doesn't exist, was deleted, or doesn't meet the publishing conditions, you get 404. Nothing validates the format of the entry ID on its own. Whatever the format, you get 404 when no entry matches.
Fetch an Object API
For Object APIs such as profile, fetch without an ID. Like a single entry, the response is the object itself.
curl 'https://api.genko.me/v1/your-workspace/profile' \ -H "X-API-KEY: ${GENKO_API_KEY}"If it hasn't been saved yet or isn't published, you get 404. You can't add /{entryId} to an Object API.
Publishing conditions and preview
A regular request returns entries that are Published, or Scheduled and past their publish start date. Entries past their publish end date are excluded. The delivery API also checks the time of the request.
Unpublished changes saved on a published entry aren't included in regular list, single, or object responses. A preview with draftKey returns the saved unpublished changes.
For draft preview, use the X-Draft-Key header on a single-entry or object request. The draftKey query parameter also works; the header takes precedence when both are present. You also need a regular API key. Passing a draft key on a list doesn't turn it into a list of drafts. Do not share preview URLs, and regenerate the key after publishing.
curl 'https://api.genko.me/v1/your-workspace/blog/abcDEF123456' \ -H "X-API-KEY: ${GENKO_API_KEY}" \ -H "X-Draft-Key: ${GENKO_DRAFT_KEY}"A draft key is 1 to 64 characters. A malformed key returns 400, and a well-formed key with no matching entry returns 404. See Draft preview for how to set it up.
Query parameters
| Request | Parameters |
|---|---|
| List | limit, offset, orders, filters, q, depth, fields, lang, lang_fallback, include |
| Single entry and object | depth, fields, draftKey, lang, lang_fallback, include |
Defaults, operators, and limits are in the query reference, and the value for each field type is in Response values.
Multilingual delivery
For an API with multiple languages enabled, set lang to one of the workspace's additional language codes. If you omit it or pass the main language, you get the main-language result as before. Each language version has its own status and publish period. Even if the main language is a draft, you can deliver only the live additional-language version.
curl --get 'https://api.genko.me/v1/your-workspace/blog/abcDEF123456' \ -H "X-API-KEY: ${GENKO_API_KEY}" \ --data-urlencode 'lang=en' \ --data-urlencode 'include=_langs'{ "id": "abcDEF123456", "title": "First article", "createdAt": "2026-09-01T00:00:00.000Z", "updatedAt": "2026-09-01T01:00:00.000Z", "publishedAt": "2026-09-01T01:00:00.000Z", "_langs": { "current": "en", "available": ["ja", "en"] }}For a list, use /v1/your-workspace/blog?lang=en. For an Object API, use /v1/your-workspace/profile?lang=en. If there is no published additional-language version, the entry is left out of a list, and single and object requests return 404. Only when you add lang_fallback=true do you get the live main-language version instead. For a list, search, filters, sort order, and totalCount are calculated from the language each row actually returns.
curl --get 'https://api.genko.me/v1/your-workspace/blog' \ -H "X-API-KEY: ${GENKO_API_KEY}" \ --data-urlencode 'lang=en' \ --data-urlencode 'lang_fallback=true' \ --data-urlencode 'include=_langs'A row whose English version is unpublished while its main-language version (Japanese, here) is live comes back like this. The title is the main-language (Japanese) value, shown as is.
{ "id": "abcDEF123456", "title": "はじめての記事", "createdAt": "2026-09-01T00:00:00.000Z", "updatedAt": "2026-09-01T01:00:00.000Z", "publishedAt": "2026-09-01T01:00:00.000Z", "_langs": { "current": "ja", "available": ["ja"] }}For a row that fell back, _langs.current is the main language, and available lists the languages that are actually live. Without include=_langs, _langs isn't added. For draft preview, pass that language version's draftKey together with lang. Preview doesn't fall back. See the setup guide as well.
Fetch the schema
_schema returns the API definitions the CLI needs to generate types. If the API key is limited to certain APIs, it returns only the allowed ones. It doesn't return entry content.
curl 'https://api.genko.me/v1/your-workspace/_schema' \ -H "X-API-KEY: ${GENKO_API_KEY}"{ "workspace": "your-workspace", "apis": [ { "name": "Blog", "endpoint": "blog", "kind": "list", "fields": [ { "id": "title", "name": "Title", "type": "text", "required": true } ] } ]}kind is list or object.
Fetch entry view counts
GET /v1/{workspace}/_views/{endpoint} requires an API key. It returns all-time and recent counts for entries belonging to that API.
curl --get 'https://api.genko.me/v1/your-workspace/_views/blog' \ -H "X-API-KEY: ${GENKO_API_KEY}" \ --data-urlencode 'ids=abcDEF123456,another-entry' \ --data-urlencode 'days=30'| Parameter | Default | Limits |
|---|---|---|
ids | Required | 1–100 comma-separated IDs, each matching ^[A-Za-z0-9_-]{1,64}$ |
days | 30 | Integer from 1 to 365; calendar days in JST, including today |
{ "days": 30, "views": { "abcDEF123456": { "total": 120, "recent": 42 }, "another-entry": { "total": 0, "recent": 0 } }}total is the all-time count; recent is the sum over the requested day range. IDs that aren't entries of this API return zero for both. Counts are keyed by public entry ID across the workspace, so entries with the same ID in different APIs share a count. In the SDK, use genko.apis.blog.views({ ids: ["abcDEF123456"], days: 30 }). The low-level client exposes client.getViews(...); see the SDK reference.
Public page-view endpoints
The /pv endpoints below need no API key. They use the same base URL, https://api.genko.me. See Page views for setup and components.
Send a view
Send a JSON string to POST /pv/{workspace} with Content-Type: text/plain.
{ "path": "/blog/x", "entryId": "abcDEF123456" }path is required and entryId is optional. Without entryId, only the path count increases; with it, only the entry count increases. Send separate requests to count both. Only Origins from registered domains are accepted. The response is always 204, including ignored localhost, bot, duplicate, and rate-limited requests. A 204 does not confirm that a view was counted.
Read all-time counts
curl --get 'https://api.genko.me/pv/your-workspace/counts' \ --data-urlencode 'entryId=a' \ --data-urlencode 'entryId=b' \ --data-urlencode 'path=/x'{ "entries": { "a": 12, "b": 0 }, "paths": { "/x": 24 } }GET /pv/{workspace}/counts accepts up to 50 entryId values and 50 path values. Repeat each parameter rather than joining values with commas. The result contains all-time counts keyed by the original requested strings; missing counts and malformed values return zero. Exceeding either limit returns 400; an unknown workspace returns 404.
Public count responses include Access-Control-Allow-Origin: * and Cache-Control: public, max-age=60, s-maxage=300. Displayed counts can lag by up to about five minutes.
Errors and operations
Every authenticated /v1 response, whether list, single entry, object, or schema, carries Cache-Control: private, no-store. This includes 400 and 404 responses returned after authentication.
On failure the API returns JSON shaped like { "message": "..." }. Handle authentication, rate-limit, and invalid-query errors separately. Errors and limits lists what to do for each status.
The API doesn't update your app's cache for you. To reflect updates, combine webhooks with your app's refetch policy.
Last updated: October 19, 2026