REST quickstart
Job Search is the available product. Use the current product metadata and quote; planned products are not callable. Prefer the Console first-task guide for a guided flow, or choose an agent/workflow kit. The requests below use Bash, Node.js for key generation, and an API key stored in TRUEFETCH_API_KEY. Keep secrets and saved input out of shared shell history, logs and source control.
1. Create an API Key
Section titled “1. Create an API Key”Sign in, open Developer → API Keys, and create a key with the required scope and monthly budget. The secret appears once. Store it in your application’s secret manager; it cannot be recovered from a command receipt.
2. Obtain a quote
Section titled “2. Obtain a quote”Send an authenticated request to POST /v1/quotes:
{ "productId": "job-search", "input": { "keyword": "Software engineer", "country": "Germany", "maxResults": 3, "maxCredits": 3, "allowPartial": false, "platforms": ["arbeitnow"] }}Save the approved JSON above as job-input.json containing the input object only, then obtain the quote:
node --input-type=module -e 'import fs from "node:fs"; process.stdout.write(JSON.stringify({productId:"job-search",input:JSON.parse(fs.readFileSync("job-input.json","utf8"))}))' > quote-request.jsoncurl --fail-with-body --max-time 30 --max-redirs 0 \ https://api.truefetch.io/v1/quotes \ -H "Authorization: Bearer $TRUEFETCH_API_KEY" \ -H 'Content-Type: application/json' \ --data-binary @quote-request.json > quote.jsonReview the returned normalized input, price version, integer credit units, limits and expiry. A quote does not execute a task, reserve credits or promise a record count.
maxResults is the ceiling for each selected platform. The quote’s resultLimit is the total ceiling, equal to maxResults multiplied by the number of resolved platforms. The total cannot exceed 1,000; a larger request is rejected without silently reducing it. Review the resolved input.platforms, especially when you omit an explicit selection.
3. Submit exactly one command
Section titled “3. Submit exactly one command”Create a new canonical Idempotency-Key immediately before a new submission:
v1.<Unix milliseconds>.<random base64url nonce>
The nonce must contain 22–64 base64url characters. Keep the entire original key unchanged through recovery. The acceptance window is 24 hours; the server allows at most five minutes of future clock skew. Generate this file once for approved new work, and retain it with the exact input before submission:
node --input-type=module -e 'import {randomBytes} from "node:crypto"; import fs from "node:fs"; fs.writeFileSync("original-command-key.txt",`v1.${Date.now()}.${randomBytes(24).toString("base64url")}`,{flag:"wx"})'flag: "wx" refuses to replace a saved key. A restart or uncertain response must use that existing file. Do not rerun key generation to discover whether work was accepted.
Send POST /v1/tasks with your Bearer API Key, the canonical Idempotency-Key header, and:
{ "productId": "job-search", "input": { "keyword": "Software engineer", "country": "Germany", "maxResults": 3, "maxCredits": 3, "allowPartial": false, "platforms": ["arbeitnow"] }, "expectedPricingVersion": "<version returned by the quote>"}After reviewing quote.json, prepare the submission using its current price version:
node --input-type=module -e 'import fs from "node:fs"; const estimate=JSON.parse(fs.readFileSync("quote.json","utf8")).estimate; const input=JSON.parse(fs.readFileSync("job-input.json","utf8")); if(!estimate||estimate.productId!=="job-search"||typeof estimate.pricingVersion!=="string"||!estimate.pricingVersion||!Number.isFinite(Date.parse(estimate.expiresAt))||Date.parse(estimate.expiresAt)<=Date.now()) throw Error("Review a valid current quote first"); process.stdout.write(JSON.stringify({productId:"job-search",input,expectedPricingVersion:estimate.pricingVersion}))' > task-request.jsoncurl --fail-with-body --max-time 30 --max-redirs 0 \ https://api.truefetch.io/v1/tasks \ -H "Authorization: Bearer $TRUEFETCH_API_KEY" \ -H "Idempotency-Key: $(cat original-command-key.txt)" \ -H 'Content-Type: application/json' \ --data-binary @task-request.json > submission.jsonThis is a deliberate new submission. Do not wrap it in a retry loop. HTTP 200 can contain a processing, rejected or otherwise unconfirmed receipt; inspect receipt.outcome before using a Task reference. The server checks the quote version and budget. Framework kits additionally check quote-input/price consistency before posting.
An accepted command returns a receipt and task reference. Its frozen contract records price, budget and retention. Do not infer a successful submission from a network timeout, or create a fresh key to retry uncertain work.
4. Recover a lost response
Section titled “4. Recover a lost response”Read GET /v1/commands/tasks.create/{originalKey} with the same authorized account. This read needs no input or fresh quote and never creates work:
curl --fail-with-body --max-time 30 --max-redirs 0 \ "https://api.truefetch.io/v1/commands/tasks.create/$(cat original-command-key.txt)" \ -H "Authorization: Bearer $TRUEFETCH_API_KEY" > original-receipt.jsonaccepted: persist the actual task reference inreceipt.result.objects. A current Task projection can be null; its absence does not erase accepted evidence.processing: wait for the stated Retry-After, then explicitly check again.rejected: read the reason and correct the input before a new command.unknown: the original request may still be in flight. Check again or explicitly seal within its acceptance window.not_accepted_final: the server has sealed the original key; re-enter your inputs and create a new key for new work.receipt_expired: the server cannot establish whether the old command was accepted. Check Tasks or contact support.
See command recovery. Terminal receipts are retained for at least seven days and may still be read after the new-command acceptance window ends. A task’s current state does not prove the outcome of a particular create or cancel command.
5. Follow the existing task
Section titled “5. Follow the existing task”Read GET /v1/tasks/{taskId}. Nonterminal tasks provide a retry interval. Failures do not authorize submitting duplicate work. Inspect source outcomes and the frozen billing facts at termination.
Set TASK_ID to the accepted receipt’s Task reference, then read:
curl --fail-with-body --max-time 30 --max-redirs 0 \ "https://api.truefetch.io/v1/tasks/$TASK_ID" \ -H "Authorization: Bearer $TRUEFETCH_API_KEY"6. Read or download results
Section titled “6. Read or download results”Use GET /v1/tasks/{taskId}/results?pageSize=50. Follow opaque continuation cursors without modifying them. The API returns bounded pages and does not collect or charge again when you read saved results.
curl --fail-with-body --max-time 30 --max-redirs 0 \ "https://api.truefetch.io/v1/tasks/$TASK_ID/results?pageSize=50" \ -H "Authorization: Bearer $TRUEFETCH_API_KEY" > result-page.jsonA single page is not a complete export. Continue only with the returned meta.pagination.nextCursor, URL-encoded as one opaque parameter, until hasMore is false and the received count agrees with the committed result count. A partial collection remains partial even after every retained record has been read.
Request fresh JSONL links through POST /v1/tasks/{taskId}/download with {"format":"jsonl"}. Download CSV through authenticated GET /v1/tasks/{taskId}/download.csv; the server streams the saved records. JSONL links are short-lived bearer credentials. Results expire seven days after terminal result commit; use the actual expiry on the task and download files you need before then. An interrupted stream is not a complete file.
Request cancellation separately
Section titled “Request cancellation separately”Cancellation uses its own newly generated, persisted canonical key; use a separate file instead of replacing original-command-key.txt. It sends POST /v1/tasks/{taskId}/cancel with {}:
curl --fail-with-body --max-time 30 --max-redirs 0 \ "https://api.truefetch.io/v1/tasks/$TASK_ID/cancel" \ -H "Authorization: Bearer $TRUEFETCH_API_KEY" \ -H "Idempotency-Key: $(cat cancellation-command-key.txt)" \ -H 'Content-Type: application/json' --data '{}' > cancellation.jsonIf the response is lost, explicitly read /v1/commands/tasks.cancel/{originalCancellationKey}. Accepted cancellation does not promise a cancelled final Task; inspect current Task execution status separately. These requests never recover a one-time key secret or restore expired results.
Result formats and retention · Source coverage and limits · Billing · Errors and next steps · Contact support