Common Patterns

Bulk-fetch verified Claims, request and track reproductions, handle 429 backoff, index arXiv papers, and embed badges in your README.

Bulk-fetch verified Claims

List public repositories first (sorted by updatedAt descending, up to 50 entries; each entry includes claimCount, verifiedClaimCount, and runCount summaries for rough filtering), then fetch each snapshot and filter verified Claims with jq:

# List owner/slug of public repositories
curl https://citeark.com/api/repositories \
  | jq -r '.repositories[] | "\(.owner)/\(.slug)"'

# Fetch one repository snapshot and filter verified claims
curl "https://citeark.com/api/repositories?owner=<owner>&slug=<slug>" \
  | jq '.repository.claims[] | select(.verification == "verified")'

Request a reproduction

Submit a run request with the repository.id from the snapshot and the claim.id you want verified. A 202 response means the run is queued; afterwards, re-fetch the snapshot to track state changes in runs (full flow below):

curl -X POST https://citeark.com/api/runs \
  -H "x-api-key: $CITEARK_API_KEY" \
  -H "content-type: application/json" \
  -d '{"repositoryId": "…", "claimId": "CLM-001"}'

This is currently a controlled private beta: only experiments using the platform-controlled executor (builtin) are accepted. License metadata controls source redistribution, not isolated execution. At most 10 submissions per hour. General-purpose Agent reproduction of arbitrary third-party code is not open yet.

Embed a badge in your README

Badges are issued live by the platform from the repository's current data (SVG, cached for 5 minutes). Link the badge back to the repository page so readers can drill into Claims, run records, and evidence:

[![CiteArk](https://citeark.com/api/badge/<owner>/<slug>)](https://citeark.com/r/<owner>/<slug>)

See Badges & Embedding for how levels are determined.

Request and track a full reproduction

Chain the earlier steps into a complete flow: read snapshot → submit run → poll status → verify signature.

  1. GET /api/repositories?owner&slug to read the snapshot; note repository.id and the target claim.id;
  2. POST /api/runs to submit the run; take run.id from the 202 response — state is queued at this point;
  3. Poll the same snapshot endpoint and watch that run's state in runs[] (poll no faster than every 10 seconds);
  4. Once state reaches a terminal verified / failed, read run.attestation; if you need independent signature verification, call GET /api/attestations/{digest} by digest.
OWNER="…"   # repository owner
SLUG="…"    # repository slug

# 1) Read the snapshot; note repository.id and the target claim.id
SNAPSHOT=$(curl -s "https://citeark.com/api/repositories?owner=$OWNER&slug=$SLUG")
REPO_ID=$(echo "$SNAPSHOT" | jq -r '.repository.id')
CLAIM_ID=$(echo "$SNAPSHOT" | jq -r '.repository.claims[0].id')

# 2) Submit the run; take run.id from the 202 response (state=queued)
RUN_ID=$(curl -s -X POST https://citeark.com/api/runs \
  -H "x-api-key: $CITEARK_API_KEY" \
  -H "content-type: application/json" \
  -d "{\"repositoryId\": \"$REPO_ID\", \"claimId\": \"$CLAIM_ID\"}" \
  | jq -r '.run.id')

# 3) Poll the same snapshot endpoint; extract this run's state with jq
while true; do
  STATE=$(curl -s "https://citeark.com/api/repositories?owner=$OWNER&slug=$SLUG" \
    | jq -r --arg id "$RUN_ID" '.repository.runs[] | select(.id == $id) | .state')
  echo "run $RUN_ID state: $STATE"
  case "$STATE" in verified|failed) break ;; esac
  sleep 10
done

# 4) After a terminal state, verify the signature by digest (the attestations path parameter drops the sha256: prefix)
DIGEST=$(curl -s "https://citeark.com/api/repositories?owner=$OWNER&slug=$SLUG" \
  | jq -r --arg id "$RUN_ID" '.repository.runs[] | select(.id == $id) | .attestation.statementDigest' \
  | sed 's/^sha256://')
curl -s "https://citeark.com/api/attestations/$DIGEST" | jq '.verified'

Handle 429s and back off

When limits are exceeded, endpoints return 429 with two kinds of headers that call for different backoff strategies (see Rate Limits & Quotas for details):

TypeResponse headersHow to handle
Rate limitRetry-After, X-RateLimit-Limit, X-RateLimit-RemainingRecovers automatically after the window; wait Retry-After seconds and retry
Monthly quotaRetry-After, X-Quota-Limit, X-Quota-Used, X-Quota-ResetResets only next month (Retry-After points at the UTC reset time in X-Quota-Reset); stop retrying and surface the error
HEADERS=$(mktemp)
while true; do
  STATUS=$(curl -s -D "$HEADERS" -o /tmp/citeark-body.json -w '%{http_code}' \
    https://citeark.com/api/repositories)
  [ "$STATUS" != "429" ] && break

  if grep -qi '^X-Quota-Limit:' "$HEADERS"; then
    # Monthly quota exhausted: retrying is pointless; wait for the next-month reset indicated by X-Quota-Reset
    echo "Monthly quota exhausted; resets at:" \
      "$(grep -i '^X-Quota-Reset:' "$HEADERS" | tr -d '\r' | awk '{print $2}')" >&2
    exit 1
  fi

  # Rate limit: back off per Retry-After (seconds), then retry
  RETRY_AFTER=$(grep -i '^Retry-After:' "$HEADERS" | tr -d '\r' | awk '{print $2}')
  sleep "${RETRY_AFTER:-60}"
done
cat /tmp/citeark-body.json

Index an arXiv paper

arXiv papers need no file upload — indexing takes three steps:

  1. GET /api/arxiv?input=2401.12345 to fetch metadata and the suggestedSlug (requires read scope, so an API Key is required);
  2. POST /api/repositories with {"input": "2401.12345"} — title, abstract, license, and PDF are all extracted server-side;
  3. GET /api/processing?repositoryId=… to poll processing status until job.state becomes completed.
# 1) Query arXiv metadata and suggestedSlug (requires read scope)
curl "https://citeark.com/api/arxiv?input=2401.12345" \
  -H "x-api-key: $CITEARK_API_KEY" | jq '.metadata.suggestedSlug'

# 2) Submit for indexing; returns 201 with repository.id
curl -X POST https://citeark.com/api/repositories \
  -H "x-api-key: $CITEARK_API_KEY" \
  -H "content-type: application/json" \
  -d '{"input": "2401.12345"}'

# 3) Poll processing status until completed
curl "https://citeark.com/api/processing?repositoryId=<repository-id>" \
  -H "x-api-key: $CITEARK_API_KEY" | jq '.job.state'

Track a private or organization repository

Pass visibility=private or owner=<org-slug> to POST /api/repositories to create the repository in your private space or under an organization. Reading private repositories requires the corresponding permission; unauthorized public queries only see the projected information (see the public projection section in Core Concepts).