List the workspace's extractions
#!/usr/bin/env bashset -euo pipefailAPI=https://api.speccapture.comAUTH="Authorization: Bearer $SPECCAPTURE_API_KEY"
page=$(curl -sS --fail-with-body "$API/v1/extractions?status=completed&limit=20" \ -H "$AUTH") || { echo "$page" >&2; exit 1; }
jq -r '.data[] as $job | "\($job.createdAt) \($job.fileName): \($job.sheetCount) sheets, \($job.progress.sheetsNeedingReview) waiting for a person"' <<<"$page"
if [[ "$(jq -r '.nextCursor' <<<"$page")" != null ]]; then jq -r '"Older jobs: send cursor=\(.nextCursor)"' <<<"$page"fiusing System.Text.Json;
using HttpClient http = new() { BaseAddress = new Uri("https://api.speccapture.com") };http.DefaultRequestHeaders.Authorization = new("Bearer", Environment.GetEnvironmentVariable("SPECCAPTURE_API_KEY"));
JsonElement page = await Json(await Call(HttpMethod.Get, "/v1/extractions?status=completed&limit=20"));
foreach (JsonElement job in page.GetProperty("data").EnumerateArray()) { Console.WriteLine($"{job.GetProperty("createdAt")} {job.GetProperty("fileName")}: {job.GetProperty("sheetCount")} sheets, {job.GetProperty("progress").GetProperty("sheetsNeedingReview")} waiting for a person");}
if (page.GetProperty("nextCursor").ValueKind != JsonValueKind.Null) { Console.WriteLine($"Older jobs: send cursor={page.GetProperty("nextCursor")}");}
// Sends a request; stops with the problem's code and detail if the API refuses it.async Task<HttpResponseMessage> Call(HttpMethod method, string path, HttpContent? body = null, string? idempotencyKey = null) { using HttpRequestMessage request = new(method, path) { Content = body }; if (idempotencyKey is not null) request.Headers.Add("Idempotency-Key", idempotencyKey); HttpResponseMessage response = await http.SendAsync(request); if (!response.IsSuccessStatusCode) { JsonElement problem = await Json(response); string? detail = (problem.TryGetProperty("detail", out JsonElement text) ? text : problem.GetProperty("title")).GetString(); Console.Error.WriteLine($"{(int)response.StatusCode} {problem.GetProperty("code")}: {detail}"); Environment.Exit(1); } return response;}
// Reads an answer as JSON.static async Task<JsonElement> Json(HttpResponseMessage response) => JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;import osimport sys
import requests
API = "https://api.speccapture.com"session = requests.Session()session.headers["Authorization"] = f"Bearer {os.environ['SPECCAPTURE_API_KEY']}"
def call(method, path, **kwargs): """Send a request; stop with the problem's code and detail if the API refuses it.""" response = session.request(method, API + path, **kwargs) if not response.ok: problem = response.json() sys.exit(f"{response.status_code} {problem['code']}: {problem.get('detail', problem['title'])}") return response
page = call("GET", "/v1/extractions?status=completed&limit=20").json()
for job in page["data"]: print(f"{job['createdAt']} {job['fileName']}: {job['sheetCount']} sheets, {job['progress']['sheetsNeedingReview']} waiting for a person")
if page["nextCursor"] is not None: print(f"Older jobs: send cursor={page['nextCursor']}")const API = "https://api.speccapture.com";const headers = { Authorization: `Bearer ${process.env.SPECCAPTURE_API_KEY}` };
const page = await call("GET", "/v1/extractions?status=completed&limit=20").then((response) => response.json());
for (const job of page.data) { console.log(`${job.createdAt} ${job.fileName}: ${job.sheetCount} sheets, ${job.progress.sheetsNeedingReview} waiting for a person`);}
if (page.nextCursor !== null) { console.log(`Older jobs: send cursor=${page.nextCursor}`);}
// Sends a request; stops with the problem's code and detail if the API refuses it.async function call(method, path, init = {}) { const response = await fetch(API + path, { ...init, method, headers: { ...headers, ...init.headers } }); if (!response.ok) { const problem = await response.json(); console.error(`${response.status} ${problem.code}: ${problem.detail ?? problem.title}`); process.exit(1); } return response;}Every job of the workspace, newest first - sent through the API, an assistant or uploaded in the dashboard - each as GET /v1/extractions/{jobId} answers it without its sheets: status, file name, size and SHA-256, template, sheet count, credits, progress (sheets read, failed, needing review), markedControlled, when it was made, updated and finished, why it failed, when its drawing was purged, and url, where the job and its sheets are. A deleted job is not listed. Page with limit and cursor (the previous page’s nextCursor), narrow with createdAfter, createdBefore, status and fileName (part of the file name, in any case). sc_test_ keys answer 501 test_mode_not_available until test-key replay exists.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Query Parameters
Section titled “Query Parameters”How many items the page holds: 1 to 200, 50 when left out.
The previous page’s nextCursor, exactly as it came: the list continues just after that page’s last item, so items made or removed meanwhile never shift a page. Leave it out for the first page.
Only items made strictly after this date and time (ISO 8601, such as 2026-10-14T09:30:00Z; without an offset it is UTC).
Only items made strictly before this date and time (ISO 8601, such as 2026-10-14T09:30:00Z; without an offset it is UTC).
Only items with this status.
Only jobs whose file name contains this, anywhere and in any case; at most 260 characters.
Responses
Section titled “Responses”OK
object
object
object
object
Bad Request: a problem with a stable code. See Errors for every code and what to do about it.
Every error: a problem with a stable code. https://speccapture.com/docs/api/errors lists every code.
object
A link to the code’s entry in the docs.
What went wrong, as a stable code to branch on: a published code never changes meaning.
This request’s trace: quote it when asking for help.
Not Implemented: a problem with a stable code. See Errors for every code and what to do about it.
Every error: a problem with a stable code. https://speccapture.com/docs/api/errors lists every code.
object
A link to the code’s entry in the docs.
What went wrong, as a stable code to branch on: a published code never changes meaning.
This request’s trace: quote it when asking for help.