Founding keys are opena year free at 6,000 req/hour, for the first 1,000 developersClaim yours
Documentation 29

Verified quickstarts · 4 min read

Every snippet here has been run.

10 clients, each proved against the live hosts by a script that executes the snippet on this page — the same string, not a copy of it — and fails if the response is not a 2xx or if what it prints is not the olympus-labs data set. Paste one and it works, or this page is wrong and the check says so.

01Install the pinned client
02Paste the snippet
03See olympus-labs come back

01 / What verified means

A script runs these, not a person.

Each entry below carries its install line, the exact code, the requests that code sends, and the values a check asserts in its output. apps/web/scripts/verify-quickstarts.ts imports the same module this page renders, writes each snippet to a file, runs it in a real Python or Node runtime with the client at the version named, and fails the run on a non-zero exit or on missing output. Every client here raises on a non-2xx, so a coverage 404 or a read-only refusal fails the check as loudly as a 500 would.

terminal
pnpm --filter @sandboxapis/web verify:quickstarts

It is not part of CI — it talks to the live hosts over the internet, and a build must not depend on the network. It is run by hand after any change to a renderer, a client pin, or a snippet. One full run is 30 requests.

02 / Keys

Anonymous works. A free key works better.

Every snippet below sends the literal token any-token, because any token is accepted — there is nothing real behind these hosts to leak or break. Anonymous callers get 60 req/hour — one bucket across every host, not one per host — which is enough to paste a quickstart and look around; a free key raises that to 600 req/hour. Each entry names the slot your key goes in.

Get a free key → · Keys & rate limits →

03 / GitHub

GitHub, drop-in.

Base URL https://gh.sandboxapis.dev. The endpoint-by-endpoint coverage, the pins and the spec of record are on the GitHub reference page; the narrative quickstart is at /docs/github.

@octokit/rest

22.0.1gh.sandboxapis.dev
install.sh
npm install @octokit/rest@22.0.1
javascript
import { Octokit } from "@octokit/rest";

const octokit = new Octokit({
  baseUrl: "https://gh.sandboxapis.dev",   // the only line that changes
  auth: "any-token",                       // any token is accepted
});

const { data: repo } = await octokit.repos.get({
  owner: "olympus-labs",
  repo: "parthenon",
});
const { data: prs } = await octokit.pulls.list({
  owner: "olympus-labs",
  repo: "parthenon",
  state: "closed",
  per_page: 5,
});

console.log(repo.full_name, "-", repo.description);
console.log(prs.length, "closed PRs, newest: #" + prs[0].number, prs[0].title);
Sends
  • GET /repos/olympus-labs/parthenon
  • GET /repos/olympus-labs/parthenon/pulls?state=closed&per_page=5
Prints
the repository's full path and description, then the five most recently closed pull requests.
Asserted
olympus-labs/parthenonclosed PRs, newest: #
Your key
Replace any-token in auth — it rides GitHub's own Authorization header.

gh CLI

2.83.2gh.sandboxapis.dev
install.sh
brew install gh   # or see cli.github.com
quickstart.sh
export GH_HOST=gh.sandboxapis.dev
export GH_ENTERPRISE_TOKEN=any-token
export GH_CONFIG_DIR=$(mktemp -d)   # keep this out of your real gh config

gh repo view olympus-labs/parthenon \
  --json nameWithOwner,description \
  --template '{{.nameWithOwner}} - {{.description}}{{"\n"}}'

gh pr list --repo olympus-labs/parthenon --state merged --limit 3
Sends
  • POST /api/graphql (gh repo view)
  • POST /api/graphql (gh pr list)
Prints
the repository line, then the three most recently merged pull requests with their head branches.
Asserted
olympus-labs/parthenonMERGED
Your key
Replace any-token in GH_ENTERPRISE_TOKEN — gh treats any non-github.com host as Enterprise Server.

gh addresses a custom host through the Enterprise Server layout (/api/v3, /api/graphql); the GitHub hosts serve that layout as an alias, so no path prefix reaches your code.

04 / GitLab

GitLab, drop-in.

Base URL https://gl.sandboxapis.dev. The endpoint-by-endpoint coverage, the pins and the spec of record are on the GitLab reference page; the narrative quickstart is at /docs/gitlab.

python-gitlab

6.5.0gl.sandboxapis.dev
install.sh
pip install python-gitlab==6.5.0
python
import gitlab

gl = gitlab.Gitlab(
    "https://gl.sandboxapis.dev",   # the only line that changes
    private_token="any-token",      # any token is accepted
)

project = gl.projects.get("olympus-labs/parthenon")
mrs = project.mergerequests.list(state="merged", per_page=5, get_all=False)

print(project.path_with_namespace, "-", project.description)
print(len(mrs), "merged MRs, newest:", mrs[0].title)
Sends
  • GET /api/v4/projects/olympus-labs%2Fparthenon
  • GET /api/v4/projects/{id}/merge_requests?state=merged&per_page=5
Prints
the project's full path and description, then the five most recently merged merge requests.
Asserted
olympus-labs/parthenonmerged MRs, newest:
Your key
Replace any-token in private_token — it rides GitLab's own PRIVATE-TOKEN header.

@gitbeaker/rest

43.8.0gl.sandboxapis.dev
install.sh
npm install @gitbeaker/rest@43.8.0
javascript
import { Gitlab } from "@gitbeaker/rest";

const api = new Gitlab({
  host: "https://gl.sandboxapis.dev",   // the only line that changes
  token: "any-token",                   // any token is accepted
});

const project = await api.Projects.show("olympus-labs/parthenon");
const mrs = await api.MergeRequests.all({
  projectId: project.id,
  state: "merged",
  perPage: 5,
  maxPages: 1,
});

console.log(project.path_with_namespace, "-", project.description);
console.log(mrs.length, "merged MRs, newest:", mrs[0].title);
Sends
  • GET /api/v4/projects/olympus-labs%2Fparthenon
  • GET /api/v4/projects/{id}/merge_requests?state=merged&per_page=5
Prints
the project's full path and description, then the five most recently merged merge requests.
Asserted
olympus-labs/parthenonmerged MRs, newest:
Your key
Replace any-token in token — gitbeaker sends it as PRIVATE-TOKEN.

05 / Bitbucket

Bitbucket, drop-in.

Base URL https://bb.sandboxapis.dev. The endpoint-by-endpoint coverage, the pins and the spec of record are on the Bitbucket reference page; the narrative quickstart is at /docs/bitbucket.

atlassian-python-api

5.0.4bb.sandboxapis.dev
install.sh
pip install atlassian-python-api==5.0.4
python
from atlassian.bitbucket import Cloud

bb = Cloud(
    url="https://bb.sandboxapis.dev",   # the only line that changes
    username="you@example.com",
    password="any-token",               # any app password is accepted
)

repo = bb.workspaces.get("olympus-labs").repositories.get("parthenon")
prs = [pr.title for pr in repo.pullrequests.each()]

print(repo.get_data("full_name"), "on", repo.get_data("mainbranch")["name"])
print(len(prs), "open PRs, newest:", prs[0])
Sends
  • GET /2.0/workspaces/olympus-labs
  • GET /2.0/repositories/olympus-labs/parthenon
  • GET /2.0/repositories/olympus-labs/parthenon/pullrequests/
  • GET /2.0/repositories/olympus-labs/parthenon/pullrequests/{id} (once per row — the client re-reads each)
Prints
the repository's full name and main branch, then every open pull request on it.
Asserted
olympus-labs/parthenon on mainopen PRs, newest:
Your key
Replace any-token in password — the app-password slot Bitbucket Cloud already uses.

06 / Azure DevOps

Azure DevOps, drop-in.

Base URL https://ado.sandboxapis.dev. The endpoint-by-endpoint coverage, the pins and the spec of record are on the Azure DevOps reference page; the narrative quickstart is at /docs/ado.

azure-devops-node-api

17.0.0ado.sandboxapis.dev
install.sh
npm install azure-devops-node-api@17.0.0
javascript
import * as azdev from "azure-devops-node-api";

const connection = new azdev.WebApi(
  "https://ado.sandboxapis.dev/olympus-labs",   // the organization URL
  azdev.getPersonalAccessTokenHandler("any-token"),
);

const core = await connection.getCoreApi();
const projects = await core.getProjects();
console.log("projects:", projects.map((p) => p.name).join(", "));

const git = await connection.getGitApi();
const repos = await git.getRepositories("olympus-labs");
console.log("repos:", repos.map((r) => r.name).sort().join(", "));
Sends
  • OPTIONS /olympus-labs/_apis/Location, then GET /olympus-labs/_apis/resourceAreas (the SDK's own handshake)
  • OPTIONS /olympus-labs/_apis/core, then GET /olympus-labs/_apis/projects
  • OPTIONS /olympus-labs/_apis/git, then GET /olympus-labs/olympus-labs/_apis/git/repositories
Prints
the organization's projects, then the git repositories inside the project.
Asserted
projects: olympus-labsrepos: parthenon
Your key
Replace any-token in getPersonalAccessTokenHandler — the PAT slot, sent as Basic auth.

The SDK resolves every client through the location service before its first real call. That handshake is served, so getCoreApi() and getGitApi() resolve without configuration.

azure-devops

7.1.0b4ado.sandboxapis.dev
install.sh
pip install azure-devops==7.1.0b4
python
from azure.devops.connection import Connection
from msrest.authentication import BasicAuthentication

connection = Connection(
    base_url="https://ado.sandboxapis.dev/olympus-labs",   # the organization URL
    creds=BasicAuthentication("", "any-token"),            # any PAT is accepted
)

core = connection.clients.get_core_client()
projects = core.get_projects()
print("projects:", ", ".join(sorted(p.name for p in projects)))

git = connection.clients.get_git_client()
repos = git.get_repositories("olympus-labs")
print("repos:", ", ".join(sorted(r.name for r in repos)))
Sends
  • OPTIONS /olympus-labs/_apis — the whole location catalogue in one request
  • GET /olympus-labs/_apis/resourceAreas
  • GET /olympus-labs/_apis/projects
  • GET /olympus-labs/olympus-labs/_apis/git/repositories
Prints
the organization's projects, then the git repositories inside the project.
Asserted
projects: olympus-labsrepos: parthenon
Your key
Replace any-token in the second argument to BasicAuthentication — the PAT slot, sent as Basic auth.

Connection.get_client() resolves every client through the location service before its first real call, and this SDK asks for the whole catalogue in ONE request — OPTIONS /olympus-labs/_apis, with no area segment. That form was uncovered until 2026-09-13, which made this client 404 on its first request and raise before any read; it is served now. Four requests to the Node SDK's six: the catalogue is cached, so no per-area call follows.

requests

2.32.5ado.sandboxapis.dev
install.sh
pip install requests==2.32.5
python
import requests

org = "https://ado.sandboxapis.dev/olympus-labs"   # the organization URL

s = requests.Session()
s.auth = ("", "any-token")            # any PAT is accepted
s.params = {"api-version": "7.1"}

repos = s.get(f"{org}/_apis/git/repositories").json()
repo = repos["value"][0]["name"]

prs = s.get(
    f"{org}/olympus-labs/_apis/git/repositories/{repo}/pullrequests",
    params={"searchCriteria.status": "completed"},
).json()

print(repos["count"], "repos:", repo)
print(prs["count"], "completed PRs, newest:", prs["value"][0]["title"])
Sends
  • GET /olympus-labs/_apis/git/repositories?api-version=7.1
  • GET /olympus-labs/olympus-labs/_apis/git/repositories/parthenon/pullrequests?searchCriteria.status=completed&api-version=7.1
Prints
the repository count and name, then the completed pull requests on it.
Asserted
repos: parthenoncompleted PRs, newest:
Your key
Replace any-token in the second half of s.auth — the PAT slot, sent as Basic auth.

The dependency-free path, for a script that does not want the SDK. It skips the location service entirely and addresses the routes directly, which is why it spends two requests where the SDK above spends four — and why it has to pass the project twice (/{organization}/{project}/_apis/…): the project-less pull-request route 404s, exactly as on dev.azure.com.

07 / Jira

Jira, drop-in.

Base URL https://jira.sandboxapis.dev. The endpoint-by-endpoint coverage, the pins and the spec of record are on the Jira reference page; the narrative quickstart is at /docs/jira.

jira

3.8.0jira.sandboxapis.dev
install.sh
pip install jira==3.8.0
python
from jira import JIRA

jira = JIRA(
    server="https://jira.sandboxapis.dev",              # the only line that changes
    basic_auth=("you@example.com", "any-token"),        # any pair is accepted
)

argo = jira.project("ARGO")
boards = jira.boards(projectKeyOrID="ARGO")

print(argo.key, "-", argo.name)
print(len(boards), "board:", boards[0].name, "/ id", boards[0].id)
Sends
  • GET /rest/api/2/serverInfo (the constructor's own handshake)
  • GET /rest/api/2/project/ARGO
  • GET /rest/agile/1.0/board?projectKeyOrId=ARGO&maxResults=50
Prints
the Argonauts project, then the one scrum board that belongs to it.
Asserted
ARGO - Argonauts1 board: ARGO board
Your key
Replace any-token in the second half of basic_auth — the API-token slot Jira Cloud already uses.

jira.search_issues() is NOT in this snippet on purpose: it calls the classic /rest/api/2/search, which Atlassian removed. This host answers it with the real 410 and points at the enhanced-search family, exactly as Jira Cloud does.

08 / Linear

Linear, drop-in.

Base URL https://linear.sandboxapis.dev. The endpoint-by-endpoint coverage, the pins and the spec of record are on the Linear reference page; the narrative quickstart is at /docs/linear.

fetch (Node built-in)

node 22linear.sandboxapis.dev
install.sh
# nothing to install — Node 22 has fetch
javascript
const api = async (query, variables) => {
  const res = await fetch("https://linear.sandboxapis.dev/graphql", {
    method: "POST",
    headers: { "content-type": "application/json", authorization: "any-token" },
    body: JSON.stringify({ query, variables }),
  });
  const { data, errors } = await res.json();
  if (!res.ok || errors) throw new Error(res.status + " " + JSON.stringify(errors));
  return data;
};

const { teams } = await api("{ teams(first: 10) { nodes { key name } } }");
const { issues } = await api(
  `query($key: String!) {
     issues(filter: { team: { key: { eq: $key } } }, first: 3) {
       nodes { identifier title state { name } }
     }
   }`,
  { key: "ARGO" },
);

console.log("teams:", teams.nodes.map((t) => t.key + " " + t.name).join(", "));
for (const i of issues.nodes) console.log(i.identifier, i.title, "[" + i.state.name + "]");
Sends
  • POST /graphql — teams(first: 10)
  • POST /graphql — issues(filter: { team: { key: { eq: "ARGO" } } }, first: 3)
Prints
every team key and name, then three Argonauts issues with their workflow states.
Asserted
ARGO ArgonautsORCL OraclesARGO-
Your key
Replace any-token in the authorization header — Linear takes a bare API key there, no Bearer prefix.

Linear has no REST dialect, so this is the whole client: one endpoint, the real schema subset. An uncovered field returns an explicit coverage error in errors[], never a silent null — which is why this snippet throws on errors rather than reading through them.

Next

Where to go from here.

All quickstarts → · Coverage manifest → · Tour the universe → · MCP setup →