List access keys
This operation returns the access keys of your account, newest first, one page at a time. A listing never includes a key's secret, so it is safe to log, print or hand to a review tool.
Every active key of the account is listed, whatever the calling key itself can reach - a key limited to one bucket still sees the keys for all buckets. Revoked keys are not listed, and neither are the keys STRG looks after for you: the dashboard's own key and the assistant's.
When you'd use it
- An access review. A scheduled job lists every key with its permissions, buckets and creation date, so someone can see at a glance what can reach your data.
- Cleaning up. A script finds the keys a finished project or an old CI pipeline left behind -
by
labelorcreatedAt- and passes their ids to Delete an access key. - Checking a key is ready. After creating a key, a deploy script can look at its
regionsto see whether it already works everywhere it is needed.
Request
GET /v1/access-keys?limit=100 HTTP/1.1
Host: api.strg.com
Authorization: Bearer <access key id>:<secret>
The calling key needs the Manage access keys through the API permission
(api:access-management); Authentication has the details.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | No | The most keys to return, from 1 to 100. Default 100. |
cursor | string | No | The nextCursor of the previous page. Left out for the first page. |
Any other query parameter is ignored.
Request headers
| Name | Required | Description |
|---|---|---|
Authorization | Yes | Bearer <access key id>:<secret> |
Response
200 OK, Content-Type: application/json, Cache-Control: no-store.
{
"accessKeys": [
{
"id": "STRG8C2R6V1T9Y4H7J5L",
"label": "ci-uploads",
"bucketScope": ["my-bucket"],
"keyPrefix": "builds/",
"permissions": ["objects:list", "objects:read", "objects:write"],
"origin": "api",
"createdAt": "2026-07-02T08:30:12.418Z",
"regions": { "eu-central-1": "pending" }
},
{
"id": "STRG4N7Q2K9W1X5B8M3D",
"label": "backup-server",
"bucketScope": null,
"keyPrefix": null,
"permissions": ["objects:list", "objects:read", "objects:write", "buckets:list-all"],
"origin": "dashboard",
"createdAt": "2026-06-15T10:00:00.000Z",
"regions": { "eu-central-1": "active", "us-east-1": "active" }
}
],
"accountRegions": ["eu-central-1", "us-east-1"],
"nextCursor": "MTc4MTUxNzYwMDAwMC5TVFJHNE43UTJLOVcxWDVCOE0zRA"
}
| Field | Type | Description |
|---|---|---|
accessKeys | array of objects | One page of keys, newest first. Empty when the account has no keys. |
accessKeys[].id | string | The access key id, as an S3 client sends it. |
accessKeys[].label | string or null | The key's name. |
accessKeys[].bucketScope | array of strings or null | The buckets the key reaches, by name. null for every bucket. |
accessKeys[].keyPrefix | string or null | The object key prefix the key is limited to, or null. |
accessKeys[].permissions | array of strings | The key's permissions, by their STRG names. |
accessKeys[].origin | string | Where the key was created: dashboard, mcp (an MCP client, such as an AI agent) or api (this API). |
accessKeys[].createdAt | string | When the key was created, ISO 8601 in UTC. |
accessKeys[].regions | object | Where the key is set up, by region: active once it works there, pending while it is still being set up. A region that is missing is not set up yet, or not needed by the key. |
accountRegions | array of strings | The regions you store data in. |
nextCursor | string or null | Sent back as cursor, it fetches the next page. null on the last page. |
A key works in a region once it shows active there. Whether a key is ready everywhere it is
needed comes from comparing its regions with accountRegions - or, for a key limited to
buckets, with the regions those buckets are in. When a key starts working
explains why a new key takes a moment.
New fields may appear in these objects over time, so a client that skips the fields it does not recognise keeps working.
Paging
Keys are ordered by creation time, newest first. A cursor marks a position in that order rather than a count, so a key created or deleted while you page through neither shifts nor repeats a key on a later page. A key created after the first page was fetched is not on any later page; a new listing, from the first page, includes it.
The cursor is opaque: it works when sent back exactly as it was received.
Errors
When a listing cannot be answered, the status and code below say why. Every error is an
application/problem+json body, whose shape Errors describes.
| Status | code | When |
|---|---|---|
400 | invalid_request | limit is not a whole number from 1 to 100. |
400 | invalid_cursor | cursor is not in the form a nextCursor takes - changed or cut short on the way, for example. |
401 | unauthenticated | The Authorization header is missing or malformed, the key or secret is wrong, the key is revoked, your account is closed, or the key does not hold Manage access keys through the API. The response carries WWW-Authenticate: Bearer realm="strg.com". |
429 | rate_limited | Too many authentication attempts for this key or from your address. The Retry-After header says how many seconds to wait before the next attempt. |
500 | internal_error | Something went wrong on STRG's side. The same request can be sent again a moment later. |
Examples
Each example lists every key of the account, following nextCursor until it is null. The curl version uses jq to read the pages.
- curl
- Python (requests)
- JavaScript (fetch)
cursor=""
while :; do
page=$(curl -sS --fail-with-body -G https://api.strg.com/v1/access-keys \
-H "Authorization: Bearer $AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" \
--data-urlencode "limit=100" \
${cursor:+--data-urlencode "cursor=$cursor"}) || { echo "$page" >&2; break; }
echo "$page" | jq -r '.accessKeys[] | "\(.id)\t\(.label // "")\t\(.createdAt)"'
cursor=$(echo "$page" | jq -r '.nextCursor // empty')
[ -z "$cursor" ] && break
done
import os
import requests
key_id = os.environ["AWS_ACCESS_KEY_ID"]
secret = os.environ["AWS_SECRET_ACCESS_KEY"]
headers = {"Authorization": f"Bearer {key_id}:{secret}"}
params = {"limit": 100}
while True:
response = requests.get("https://api.strg.com/v1/access-keys", headers=headers, params=params)
response.raise_for_status()
page = response.json()
for key in page["accessKeys"]:
print(key["id"], key["label"], key["createdAt"], key["regions"])
if page["nextCursor"] is None:
break
params["cursor"] = page["nextCursor"]
const headers = {
Authorization: `Bearer ${process.env.AWS_ACCESS_KEY_ID}:${process.env.AWS_SECRET_ACCESS_KEY}`,
};
let cursor = null;
do {
const url = new URL("https://api.strg.com/v1/access-keys");
url.searchParams.set("limit", "100");
if (cursor !== null) url.searchParams.set("cursor", cursor);
const response = await fetch(url, { headers });
if (!response.ok) {
const problem = await response.json();
throw new Error(`${response.status} ${problem.code}: ${problem.detail}`);
}
const page = await response.json();
for (const key of page.accessKeys) {
console.log(key.id, key.label, key.createdAt, key.regions);
}
cursor = page.nextCursor;
} while (cursor !== null);
Top-level await works when the file is saved with an .mjs extension.
Related
- Get an access key
- Create an access key
- Delete an access key
- REST API overview - authentication, errors, paging and rate limits
- Access keys - scope, prefixes and every permission