Get Started
Reference

Findings export

Download filtered project findings with report, CWE, and file provenance

Endpoint

GET /v1/projects/{projectId}/findings/export?profile=security|bugs|hygiene|pipeline|all

Needs a session or bearer token that can read the project. The UI calls this from Findings → Export JSON and downloads thespider-findings-{projectSlug}.json.

Query parameters

ParameterTypeDescription
profilestringsecurity, bugs, hygiene, pipeline, or explicit aggregate all. Omission uses the project default.
campaignIdintegerInclude live findings sighted in this campaign. An opaque id, see Identifiers. Present but malformed is 400 invalid_campaign_id; unknown or foreign is 404 campaign_not_found.
statusstringOne or more persisted finding statuses, comma-separated. Also accepts the open, actionable, and closed aliases.
severitystringOne or more canonical severity or impact values, comma-separated. Also accepts the reserved token none.
bucketstringMatch the display bucket code: the bucket the finding's classification decided (the moved-to bucket, else the discovery bucket), with the profile fallback described under Finding fields.
categorystringMatch findings with a report in this category.
branchstringMatch findings sighted on this branch.
qstringCase-insensitive title, fingerprint, or display-bucket search.
has_linktrue | falseOnly findings that have (or have no) linked merge request, pull request or issue.
basisstringEvidence basis, comma-separated: demonstrated, speculative (a hardening note), unclassified. demonstrated,unclassified hides hardening notes.
bucket_reviewtrue | falseOnly findings whose bucket is (or is not) flagged for review.
sortstringpriority (default), updated_desc, updated_asc, or title.

Filters combine with AND. The endpoint ignores list pagination fields and returns the complete filtered set up to the export safety limit.

Status and severity filters

Same rules as GET /v1/projects/{projectId}/findings. Comma-separated lists, trimmed, de-duplicated.

GET /v1/projects/{projectId}/findings/export?status=open&severity=critical,high
AliasMeaning
openEverything except fixed and false_positive
actionableEverything except wont_fix, false_positive, accepted, and fixed (what the PR gate still blocks on)
closedExactly fixed or false_positive

accepted and wont_fix stay open but drop out of actionable. Aliases and literal statuses mix (status=actionable,accepted) as a union.

severity=none matches findings with no severity. severity=critical,high excludes them; add none to include them. Unknown tokens match literally and usually return nothing.

basis and bucket_review reject anything outside their values with 400 invalid_basis / 400 invalid_bucket_review, like has_link.

filters in the response echoes the raw query strings ("open", "critical,high"), not the expanded set, including basis and bucket_review when sent. That echo is part of schema version 1. A client can tell a server that predates a filter by its missing echo.

Response

{
  "schemaVersion": 1,
  "exportedAt": "2026-07-22T12:00:00.000Z",
  "project": {
    "id": 5361849201747,
    "slug": "payments-api",
    "name": "Payments API",
    "default_profile": "security"
  },
  "profile": "security",
  "filters": {
    "campaignId": "3318402957264",
    "severity": "high"
  },
  "mergedFindingsIncluded": false,
  "total": 1,
  "findings": [
    {
      "id": 2417903825611,
      "canonical_title": "Authorization missing on refund",
      "canonical_severity": "high",
      "severity_source": "reports",
      "severity_report_id": 7391846205518,
      "severity_model_code": "anthropic/claude-sonnet-5",
      "severity_reason": "highest of 1 report: high (report #7391846205518, anthropic/claude-sonnet-5); distribution high 1",
      "status": "new",
      "fingerprint": "…",
      "bucket_id": null,
      "canonical_bucket_id": null,
      "bucket_code": "auth",
      "bucket_moved": false,
      "discovered_bucket_code": "auth",
      "canonical_category": "authz",
      "category_source": "reports",
      "bucket_review": null,
      "suggested_category": null,
      "evidence_basis": null,
      "evidence_basis_source": null,
      "evidence_basis_reason": null,
      "links": [
        { "id": 4120958831204, "url": "https://gitlab.com/acme/api/-/merge_requests/128", "provider": "gitlab",
          "kind": "merge_request", "ref": "!128", "host": "gitlab.com", "actor_user_id": null,
          "created_at": "2026-10-03T10:00:00Z" }
      ],
      "reports": [
        {
          "id": 7391846205518,
          "model_code": "anthropic/claude-sonnet-5",
          "effort": null,
          "pass_code": "http",
          "severity": "High",
          "normalized_severity": "high",
          "categories": ["authz"],
          "cwes": ["CWE-862"],
          "files": [
            { "path": "src/refunds.ts", "line": 88 }
          ],
          "evidence": "…",
          "impact": "…",
          "recommendation": "…",
          "evidence_basis": null,
          "evidence_basis_source": null,
          "recommendation_issues": []
        }
      ]
    }
  ]
}

The export includes canonical project-global finding status and only report/sighting provenance that matches the selected profile. bucket_code is the display bucket (rule below) and discovered_bucket_code is provenance: the bucket of the slice that first found the finding. Model-derived strings remain plain JSON strings.

Finding fields

Each finding is the full finding row plus campaign_name, bucket_code, bucket_name, bucket_moved, discovered_bucket_code, links and reports. The canonical quality columns:

FieldMeaning
canonical_severitycritical, high, medium, low, info, or null: the highest severity any supporting report assigned, normalized. A merge never lowers it.
severity_sourcereports, manual (set by a person; sticky) or legacy (imported or pre-upgrade, kept as a floor).
severity_report_idThe witness report's id, or null once that report was removed.
severity_model_code, severity_reasonThe witness's model and why the value won. Both survive the report's removal.
bucket_idAlways null. The key is kept so schema version 1 readers keep working; identify buckets by bucket_code.
canonical_bucket_idAlways null, like bucket_id. Use bucket_moved to tell whether the finding was moved, and bucket_code for where it is displayed.
bucket_code, bucket_nameThe display bucket: the bucket the finding's classification decided, which is the moved-to bucket when the finding was moved, else the discovery bucket. profile=all always shows it. Under a selected profile it is shown when that profile's config version owns it. Otherwise (another profile's view, or a legacy row whose bucket was never computed) it falls back to the bucket of the finding's latest report in that profile.
bucket_movedtrue when the display bucket is the moved-to bucket, else false. Missing on older servers.
discovered_bucket_codeProvenance: the discovery bucket's code (the slice that found it). It is not the display bucket.
canonical_category, category_sourceThe category most supporting reports used, and reports / manual / legacy.
bucket_review, suggested_categoryWhy the bucket is flagged for review (else null) and the suggested category.
evidence_basis, evidence_basis_source, evidence_basis_reasondemonstrated / speculative / null; model / heuristic / manual; and why.

Each report adds normalized_severity, its own evidence_basis and evidence_basis_source, and recommendation_issues ([{ code, message, excerpt }], today only sqlite3_positional_bind). These are computed when the export is read and never stored; the report's raw severity and text are exactly what the model emitted.

Each report names the participant that produced it: model_code and effort, the effort its slice was planned at (null for the model's default participant). The effort is what the runner was bound to, not something TheSpider verified. Servers before effort participants omit effort.

A report also carries the extra fields of the profile whose campaign produced it, the same keys finding detail returns. Each is a string or null. Security reports have none.

Report profileFields
bugsreproduction_steps, expected_behavior, actual_behavior, root_cause, test_suggestion
hygieneusage_evidence, removal_risk
pipelinecost_driver, verification_plan

With profile=all, one finding can hold reports from several profiles, and each report has only its own profile's keys. Servers before these fields were exported omit them.

Identifiers

Every id, *_id and actor_user_id in the export is an opaque positive integer below 2^53. Ids are unordered: never sort or compare them, and never infer how many findings, reports or campaigns exist from them. The findings array is already in the order selected by sort. Ids belonging to rows created before opaque ids were introduced keep their old values. See Identifiers.

Identity and merge behavior

The export includes live keeper findings only. Soft-merged tombstones are excluded, and mergedFindingsIncluded is always false in schema version 1. Reports absorbed by a keeper appear under that keeper.

Campaign filtering uses finding sightings. It does not filter by the campaign where a finding was first created.

Size behavior

A filtered export can contain at most 20,000 findings. A larger result returns:

413 export_too_large

Narrow the request with campaignId, severity, status, bucket, category, branch, or search filters. The endpoint returns one JSON document and does not paginate or create an asynchronous export job.

Errors

StatusErrorMeaning
400invalid_project_idThe path identifier is not a positive integer.
400invalid_campaign_idcampaignId is present but is not a valid campaign id.
400invalid_since, invalid_has_link, invalid_basis, invalid_bucket_reviewA filter value is present but not one the filter accepts. A since campaign id that is unknown or belongs to another organization is invalid_since.
401authentication errorNo valid session or bearer token.
404project_not_foundThe project does not exist in the authenticated tenant or token scope.
404campaign_not_foundcampaignId names no campaign in this project (an unknown id and another organization's id answer alike).
413export_too_largeMore than 20,000 findings match.
429rate_limitedRequest budget exhausted.

Use the triage guide for finding lifecycle behavior and the concepts glossary for keeper, merge, and sighting definitions.

Copyright © 2026