Quickstart
Last updated 28 October 2026 · API version v1
From a test key to a paged list in five steps. The key is created in the application; every command after it is shown with the complete response the sandbox gave it, and the program with what it printed, so you can check yours against this page.
Ten minutes, including creating the key.
1. Create a test key
In the application, go to Settings, API keys and choose Create key. Choose test mode and the read scope. The key starts bo_test_.
The key is shown in full exactly once. After that you see the prefix and the last four. We cannot recover it and we will not pretend to.
Keep it in an environment variable rather than in a file you might commit. Every command and program on these pages reads it from BARRIEROPS_API_KEY.
2. Make your first request
List the assets your key reaches. A test key reaches the sandbox’s City of Riverbend, a fictional city:
bash: list the assets your key reaches
curl https://api.barrierops.com/api/v1/properties \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: the complete response
{
"items": [
{
"id": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"name": "riverbend.example",
"baseUrl": "https://www.riverbend.example",
"kind": "website",
"verificationState": "verified",
"createdAt": "2026-07-01T15:40:00.000Z",
"deletedAt": null
}
],
"nextCursor": null,
"total": 1
}Lines are wrapped for printing. The original is one line.
Every list answers in this shape: items, nextCursor and total. The response is shown formatted here; the API sends it on one line.
3. Read the rate-limit headers
The same request again, keeping only the rate-limit headers:
bash: print the rate-limit headers
curl -s -D - -o /dev/null https://api.barrierops.com/api/v1/properties \
-H "Authorization: Bearer $BARRIEROPS_API_KEY" | grep -i '^x-ratelimit-'Lines are wrapped for printing. The original is one line.
http: the three headers, on a key’s first request
x-ratelimit-limit: 120
x-ratelimit-remaining: 119
x-ratelimit-reset: 1791561601Lines are wrapped for printing. The original is one line.
Errors carry all three headers too, so you never have to discover your limit by hitting it. The limit is the key’s budget a minute, set by its organisation’s edition — this key’s organisation is on Standard — and the reset is a Unix timestamp. Rate limits has every edition’s figures, and the two responses that go without the headers.
4. Page through a list
One loop: ask for a page, then ask again with the nextCursor it returned, and stop when it is null. This program reads the sandbox asset’s critical issues three to a page:
typescript: page through a list with nextCursor
// Every critical issue on the first asset a key reaches, three to a page.
// Node 22.18 or later runs this file as it is: node page-issues.ts
import { env } from 'node:process';
const BASE_URL = 'https://api.barrierops.com/api/v1';
const KEY = env['BARRIEROPS_API_KEY'];
if (KEY === undefined) {
throw new Error('Set BARRIEROPS_API_KEY to your API key first.');
}
interface Page<Item> {
items: Item[];
nextCursor: string | null;
total: number;
}
interface Asset {
id: string;
name: string;
}
interface Issue {
title: string;
viewport: string;
}
async function get<Body>(path: string): Promise<Body> {
const response = await fetch(`${BASE_URL}${path}`, {
headers: { Authorization: `Bearer ${KEY}` },
});
if (!response.ok) {
throw new Error(`${String(response.status)} from ${path}: ${await response.text()}`);
}
return (await response.json()) as Body;
}
const count = (n: number, noun: string): string => `${String(n)} ${noun}${n === 1 ? '' : 's'}`;
const [asset] = (await get<Page<Asset>>('/properties')).items;
if (asset === undefined) {
throw new Error('This key reaches no asset.');
}
let cursor: string | null = null;
let pages = 0;
let seen = 0;
do {
const query = new URLSearchParams({ impact: 'critical', limit: '3' });
if (cursor !== null) {
query.set('cursor', cursor);
}
const page: Page<Issue> = await get(`/properties/${asset.id}/issues?${query.toString()}`);
pages += 1;
seen += page.items.length;
console.log(`Page ${String(pages)}`);
for (const issue of page.items) {
console.log(` ${issue.title} (${issue.viewport})`);
}
cursor = page.nextCursor;
} while (cursor !== null);
console.log(`${count(seen, 'critical issue')} on ${asset.name}, in ${count(pages, 'page')}`);
Lines are wrapped for printing. The original is one line.
text: what it prints
Page 1
Input buttons must have discernible text (desktop)
Images must have alternative text (desktop)
Select element must have an accessible name (desktop)
Page 2
ARIA attributes must conform to valid values (desktop)
Form elements must have labels (desktop)
Images must have alternative text (desktop)
Page 3
Form elements must have labels (desktop)
Images must have alternative text (desktop)
Images must have alternative text (desktop)
Page 4
Buttons must have discernible text (desktop)
Buttons must have discernible text (desktop)
ARIA attributes must conform to valid values (desktop)
Page 5
Required ARIA attributes must be provided (desktop)
Elements must only use supported ARIA attributes (desktop)
Buttons must have discernible text (desktop)
15 critical issues on riverbend.example, in 5 pagesLines are wrapped for printing. The original is one line.
Pagination has the same loop in Python, and the first and last responses it reads.
5. Switch to live
Create a key in live mode and swap it in: bo_test_ becomes bo_live_, and every command above reads your organisation instead of the sandbox. Nothing else changes — the same base URL, the same paths, the same shapes.
Test mode never touches production data and production keys never return fixture data.
A test key also changes nothing. Asked to start a scan, it is refused, and a live key with the write scope is what starts one:
bash: start a scan with a test key
curl -X POST https://api.barrierops.com/api/v1/properties/9ccf6432-ad06-5afe-8a30-52373a0346a4/scans \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: the complete response, a 403
{
"type": "https://barrierops.app/problems/forbidden",
"title": "This key does not permit this action",
"status": 403,
"detail": "Test keys read the fixture organisation and change nothing.",
"instance": "00000000-0000-4000-8000-000000000000"
}Lines are wrapped for printing. The original is one line.
instance is the request id, which is different on every request; this page prints one fixed value in its place.