# StartupSeeker Public API V2

StartupSeeker exposes the following v2 public endpoints.

## Authentication

- API-key endpoints require `Authorization: Bearer ssk_live_...` or `ssk_test_...`.
- All public v2 endpoints are API-key-only.
- Long-running workflows return async jobs that you poll via job status/result endpoints.
- Download the machine-readable contract from `/api/openapi/v2.json`.

## Endpoint Index

- `POST /api/v2/searches` - Canonical search endpoint for natural-language startup discovery. ($0.02 per request)
- `POST /api/v2/company-lookups` - Batch company lookup endpoint for fetching company records by website/domain or by company name. ($0.00 per request)
- `POST /api/v2/general-enrichment-jobs` - Create a stateless asynchronous general startup-enrichment job. ($0.04 per request)
- `GET /api/v2/general-enrichment-jobs` - List general enrichment jobs owned by the calling API key/account. ($0.00 per request)
- `GET /api/v2/general-enrichment-jobs/{job_id}` - Get bounded status and progress for an owned general enrichment job. ($0.00 per request)
- `GET /api/v2/general-enrichment-jobs/{job_id}/result` - Retrieve the bounded terminal result for an owned general enrichment job. ($0.00 per request)
- `POST /api/v2/founder-enrichment-jobs` - Create a stateless asynchronous founders startup-enrichment job. ($0.10 per request)
- `GET /api/v2/founder-enrichment-jobs` - List founders enrichment jobs owned by the calling API key/account. ($0.00 per request)
- `GET /api/v2/founder-enrichment-jobs/{job_id}` - Get bounded status and progress for an owned founders enrichment job. ($0.00 per request)
- `GET /api/v2/founder-enrichment-jobs/{job_id}/result` - Retrieve the bounded terminal result for an owned founders enrichment job. ($0.00 per request)
- `POST /api/v2/funding-enrichment-jobs` - Create a stateless asynchronous funding startup-enrichment job. ($0.10 per request)
- `GET /api/v2/funding-enrichment-jobs` - List funding enrichment jobs owned by the calling API key/account. ($0.00 per request)
- `GET /api/v2/funding-enrichment-jobs/{job_id}` - Get bounded status and progress for an owned funding enrichment job. ($0.00 per request)
- `GET /api/v2/funding-enrichment-jobs/{job_id}/result` - Retrieve the bounded terminal result for an owned funding enrichment job. ($0.00 per request)
- `POST /api/v2/combined-enrichment-jobs` - Create a stateless asynchronous combined startup-enrichment job. ($0.20 per request)
- `GET /api/v2/combined-enrichment-jobs` - List combined enrichment jobs owned by the calling API key/account. ($0.00 per request)
- `GET /api/v2/combined-enrichment-jobs/{job_id}` - Get bounded status and progress for an owned combined enrichment job. ($0.00 per request)
- `GET /api/v2/combined-enrichment-jobs/{job_id}/result` - Retrieve the bounded terminal result for an owned combined enrichment job. ($0.00 per request)
- `POST /api/v2/competitor-analyses` - Competitor analysis endpoint with the standard v2 response envelope. ($0.02 per request)
- `POST /api/v2/vc-backability-checks` - Classify startup VC-backability synchronously for up to 50 websites or asynchronously for 51-500 websites. ($0.00 per request)
- `GET /api/v2/vc-backability-checks` - List recent VC backability async jobs owned by the authenticated API key. ($0.00 per request)
- `GET /api/v2/vc-backability-checks/{job_id}` - Poll the status of an asynchronous VC backability job. ($0.00 per request)
- `GET /api/v2/vc-backability-checks/{job_id}/result` - Fetch the completed result for an asynchronous VC backability job. ($0.00 per request)
- `GET /api/v2/pitch-deck-jobs` - List recent pitch-deck jobs owned by the authenticated API key so clients can recover saved job IDs. ($0.00 per request)
- `POST /api/v2/pitch-deck-jobs` - Create an asynchronous pitch-deck parsing job. ($0.10 per request)
- `GET /api/v2/pitch-deck-jobs/{job_id}` - Poll the status of an asynchronous pitch-deck job. ($0.00 per request)
- `GET /api/v2/pitch-deck-jobs/{job_id}/result` - Fetch the completed result for an asynchronous pitch-deck job. ($0.00 per request)
- `GET /api/v2/deep-research-jobs` - List recent deep-research jobs owned by the authenticated API key so clients can recover saved job IDs. ($0.00 per request)
- `POST /api/v2/deep-research-jobs` - Create an asynchronous deep-research job. The removed legacy `/api/deep-research` route now returns `410 Gone`; use this route instead. ($0.20 per request)
- `GET /api/v2/webhook-endpoints` - List managed webhook endpoints for the authenticated account. ($0.00 per request)
- `POST /api/v2/webhook-endpoints` - Create a managed webhook endpoint for async job completion events. ($0.00 per request)
- `DELETE /api/v2/webhook-endpoints/{webhook_id}` - Delete a managed webhook endpoint owned by the authenticated account. ($0.00 per request)
- `GET /api/v2/deep-research-jobs/{job_id}` - Poll the status of an asynchronous deep-research job. ($0.00 per request)
- `GET /api/v2/deep-research-jobs/{job_id}/result` - Fetch the completed result for an asynchronous deep-research job. ($0.00 per request)

## Endpoints

### Searches

- **Method:** `POST`
- **Path:** `/api/v2/searches`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account_with_balance`
- **USD price:** $0.02 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{
  "request_id": "search-v2-demo-1",
  "query": "AI startups helping ecommerce teams with demand forecasting",
  "query_mode": "auto",
  "limit": 10,
  "filters": {
    "foundedYear": {
      "min": 2020
    },
    "funding": {
      "max": 20000000
    },
    "employees": {
      "min": 10,
      "max": 500
    },
    "score": {
      "min": 7.5
    },
    "countries": {
      "include": ["US", "CA"]
    },
    "industries": {
      "values": ["retail", "logistics"],
      "operator": "OR"
    },
    "websites": {
      "exclude": ["example.com"]
    }
  }
}
```

**Request fields**

- `request_id` (string | null, optional) - Optional client correlation ID. Maximum 128 characters.
- `query` (string, required) - Search query. Must be between 3 and 500 characters.
- `query_mode` ("auto" | "name" | "discovery" | null, optional) - Optional routing hint for lexical company-name lookup versus semantic discovery.
- `limit` (number | null, optional) - Optional result cap between 1 and 50. Defaults to 25.
- `filters` (object | null, optional) - Optional structured filter object passed into the main search pipeline. Unknown keys are rejected.
- `filters.foundedYear` ({ min?: number | null; max?: number | null; includeMissing?: boolean }, optional) - Filter by founded year. Use `includeMissing: true` to also include companies with unknown founded year.
- `filters.lastRaisedAge` ({ operator?: "<" | ">" | "=" | "<=" | ">=" | "between"; value?: number | null; endValue?: number | null; unit?: "months" | "years"; includeMissing?: boolean }, optional) - Filter by relative time since the latest funding round.
- `filters.funding` ({ min?: number | null; max?: number | null }, optional) - Filter by total funding in USD.
- `filters.employees` ({ min?: number | null; max?: number | null }, optional) - Filter by LinkedIn employee count.
- `filters.followers` ({ min?: number | null; max?: number | null }, optional) - Filter by LinkedIn follower count.
- `filters.score` ({ min?: number | null; max?: number | null }, optional) - Filter by StartupSeeker score on the 0-10 scale.
- `filters.countries` ({ include?: string[]; exclude?: string[] }, optional) - Include or exclude ISO-2 country codes such as `US`, `DE`, or `FR`.
- `filters.cities` (string[], optional) - Restrict results to one or more city names.
- `filters.regions` (string[], optional) - Restrict results to one or more region, state, or province names.
- `filters.cityGeonameIds` (number[] | { include?: number[]; exclude?: number[] }, optional) - Restrict results to canonical GeoNames city IDs, with optional include/exclude semantics.
- `filters.admin1Codes` (string[] | { include?: string[]; exclude?: string[] }, optional) - Restrict results to canonical admin1 codes such as `US-CA`, with optional include/exclude semantics.
- `filters.regionSlugs` (string[] | { include?: string[]; exclude?: string[] }, optional) - Restrict results to canonical metro region slugs such as `sf_bay_area`, with optional include/exclude semantics.
- `filters.industries` ({ values: string[]; operator?: "AND" | "OR" }, optional) - Match one or more normalized industry/category labels. `OR` is the default when omitted.
- `filters.includedInvestors` (DatabaseInvestorFilterOption[], optional) - Restrict results to startups backed by one or more selected investors.
- `filters.excludedInvestors` (DatabaseInvestorFilterOption[], optional) - Exclude startups backed by one or more selected investors.
- `filters.lists` ({ include?: uuid[]; exclude?: uuid[] }, optional) - Include or exclude companies that belong to your saved lists by StartupSeeker list ID.
- `filters.websites` ({ include?: string[]; exclude?: string[] }, optional) - Include or exclude specific company domains. Domains are normalized before matching.

**Response fields**

- `data.query` (string, optional) - Echoes the final query used for the search.
- `data.total_results` (number, optional) - Number of returned results.
- `data.results` (array<object>, optional) - Structured company results.
- `data.active_filters` (object, optional) - Resolved filters after query interpretation. This can include any supported filter key plus `semanticQuery`.
- `data.active_filters.semanticQuery` (string, optional) - Semantic query text actually used after the refinement stage.
- `data.request` (object, optional) - Request metadata emitted by the search service.
- `data.request.query_mode_requested` (string | null, optional) - Requested query routing mode, when provided.
- `data.request.query_mode_resolved` (string, optional) - Resolved routing mode used by the search service.
- `data.request.used_lexical_lookup` (boolean, optional) - Whether lexical company-name lookup ran for this request.
- `data.request.name_match_confidence` (string | null, optional) - Present when lexical company-name lookup produced a ranked match.
- `meta` (object, optional) - Standard v2 metadata envelope with request ID, duration, rate-limit state, and billing.

**Sample request**

```bash
curl -X POST https://startup-seeker.com/api/v2/searches \
  -H "Authorization: Bearer ssk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "request_id": "search-v2-demo-1",
    "query": "AI startups helping ecommerce teams with demand forecasting",
    "query_mode": "auto",
    "limit": 10,
    "filters": {
      "foundedYear": { "min": 2020 },
      "funding": { "max": 20000000 },
      "employees": { "min": 10, "max": 500 },
      "score": { "min": 7.5 },
      "countries": { "include": ["US", "CA"] },
      "industries": { "values": ["retail", "logistics"], "operator": "OR" }
    }
  }'
```

**Sample response**

```json
{
  "data": {
    "query": "AI startups helping ecommerce teams with demand forecasting",
    "total_results": 2,
    "results": [
      {
        "primary_key": "458861125484422704",
        "name": "Euphya",
        "website": "euphya.co"
      }
    ],
    "active_filters": {
      "foundedYear": {
        "min": 2020
      },
      "funding": {
        "max": 20000000
      },
      "employees": {
        "min": 10,
        "max": 500
      },
      "score": {
        "min": 7.5
      },
      "countries": {
        "include": ["US", "CA"]
      },
      "industries": {
        "values": ["retail", "logistics"],
        "operator": "OR"
      },
      "semanticQuery": "AI startups helping ecommerce teams with demand forecasting"
    },
    "request": {
      "duration": 1420,
      "query_mode_requested": "auto",
      "query_mode_resolved": "discovery",
      "used_lexical_lookup": false,
      "name_match_confidence": null
    }
  },
  "meta": {
    "request_id": "search-v2-demo-1",
    "api_version": "v2",
    "duration_ms": 1420,
    "rate_limit": {
      "second": { "limit": 5, "remaining": 4, "reset_at": 1712083200000 },
      "minute": { "limit": 30, "remaining": 29, "reset_at": 1712083200000 },
      "day": { "limit": 100, "remaining": 99, "reset_at": 1712126400000 }
    },
    "billing": {"currency": "USD",
      "charged_usd": 0.02,
      "remaining_usd": 19.98
    }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `X-RateLimit-Limit-Second` - Effective per-second limit for the key/account.
- `X-RateLimit-Limit-Minute` - Effective per-minute limit for the key/account.
- `X-RateLimit-Limit-Day` - Effective per-day limit for the key/account.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `400 BAD_REQUEST` - Invalid JSON, invalid `query`, or invalid filter structure.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `402 INSUFFICIENT_FUNDS` - API key is valid but the account has insufficient USD balance.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.
- `500 INTERNAL_ERROR` - Unexpected search failure.

**Notes**

- Legacy v1 search has been removed. Use this v2 route for all public search integrations.
- The request `limit` is capped at `50`; values above `50` are rejected with `400 BAD_REQUEST`.
- Use `query_mode: "name"` for exact company-name lookup and `query_mode: "discovery"` to skip lexical name routing.
- Structured filters currently support founded year, funding, employee count, follower count, score, countries, cities, regions, industries, saved lists, and explicit websites.
- Use ISO-2 country codes in `filters.countries.include` / `exclude`, for example `US`, `DE`, or `FR`.
- Successful responses always use the v2 `data` + `meta` envelope.

### Company Lookups

- **Method:** `POST`
- **Path:** `/api/v2/company-lookups`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account_with_balance`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{
  "request_id": "lookup-batch-demo-1",
  "names": ["Stripe", "OpenAI"],
  "limit_per_name": 3
}
```

**Request fields**

- `request_id` (string | null, optional) - Optional client correlation ID. Maximum 128 characters.
- `websites` (array<string> | null, optional) - Supply 1 to 100 company websites or domains for direct website/domain lookup.
- `names` (array<string> | null, optional) - Supply 1 to 50 company names for lexical company-name lookup.
- `limit_per_name` (number | null, optional) - Optional for `names` lookups only. Defaults to 5 and cannot exceed 10.

**Response fields**

- `data.companies` (array<object>, optional) - Ordered lookup results in the same order as the submitted `websites` or `names` array.
- `data.companies[].lookup_kind` ("website" | "name", optional) - Indicates whether the item came from a website lookup or name lookup branch.
- `data.companies[].submitted` (string, optional) - Original submitted website or company name string.
- `data.companies[].normalized` (string, optional) - Normalized website/domain or normalized company name used for matching.
- `data.companies[].company` (object | null, optional) - Matching company record, or `null` when no match exists. For name lookups this mirrors the best match when present.
- `data.companies[].best_match` (object | null, optional) - Present on name lookups. Contains `matched_alias`, `match_type`, `confidence`, `score`, and `company`.
- `data.companies[].matches` (array<object>, optional) - Present on name lookups. Ranked lexical candidates for the submitted name.
- `data.companies[].missing` (boolean, optional) - Convenience flag indicating whether the company record was missing.
- `meta` (object, optional) - Standard v2 metadata envelope with rate-limit and balance state.

**Sample request**

```bash
curl -X POST https://startup-seeker.com/api/v2/company-lookups \
  -H "Authorization: Bearer ssk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "request_id": "lookup-batch-demo-1",
    "names": ["Stripe", "OpenAI"],
    "limit_per_name": 3
  }'
```

**Sample response**

```json
{
  "data": {
    "companies": [
      {
        "lookup_kind": "name",
        "submitted": "Stripe",
        "normalized": "stripe",
        "company": {
          "id": "458861125612149361",
          "name": "Stripe",
          "website": "stripe.com"
        },
        "best_match": {
          "matched_alias": "Stripe",
          "match_type": "exact",
          "confidence": "high",
          "score": 1000,
          "company": {
            "id": "458861125612149361",
            "name": "Stripe",
            "website": "stripe.com"
          }
        },
        "matches": [
          {
            "matched_alias": "Stripe",
            "match_type": "exact",
            "confidence": "high",
            "score": 1000,
            "company": {
              "id": "458861125612149361",
              "name": "Stripe",
              "website": "stripe.com"
            }
          }
        ],
        "missing": false
      },
      {
        "lookup_kind": "name",
        "submitted": "OpenAI",
        "normalized": "openai",
        "company": null,
        "best_match": null,
        "matches": [],
        "missing": true
      }
    ]
  },
  "meta": {
    "request_id": "lookup-batch-demo-1",
    "api_version": "v2",
    "duration_ms": 180,
    "billing": {"currency": "USD", "charged_usd": 0, "remaining_usd": 20.00 }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `X-RateLimit-Limit-Minute` - Effective per-minute limit for the key/account.
- `X-RateLimit-Limit-Day` - Effective per-day limit for the key/account.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `400 BAD_REQUEST` - Invalid JSON, providing both `websites` and `names`, providing neither, invalid array sizes, or invalid website input.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.
- `500 INTERNAL_ERROR` - Website lookup service is not configured or failed.

**Notes**

- Legacy v1 enrich has been removed. Use this v2 batch route for website or company-name lookups.
- Provide exactly one of `websites` or `names` per request.
- Results preserve input order and duplicates from the submitted array.
- A 200 response with `data.companies[].missing: true` is the expected result when StartupSeeker has no matching company.

### Start General enrichment

- **Method:** `POST`
- **Path:** `/api/v2/general-enrichment-jobs`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account_with_balance`
- **USD price:** $0.04 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{
  "website": "https://acme.example",
  "company_name": "Acme",
  "webhook_endpoint_id": "owned-webhook-uuid"
}
```

**Request fields**

- `website` (string, required) - HTTP(S) website, at most 2,048 characters.
- `request_id` (string | null, optional) - Optional caller correlation ID, at most 128 characters.
- `company_name` (string | null, optional) - Optional bounded company-name hint.
- `description` (string | null, optional) - Optional description hint, at most 20,000 characters.
- `company_aliases` (string[] | null, optional) - At most 20 unique aliases, 200 characters each.
- `company_linkedin_url` (string | null, optional) - Optional LinkedIn company URL.
- `webhook_endpoint_id` (UUID | null, optional) - Optional webhook endpoint owned by this account.

**Response fields**

- `data.job_id` (UUID, optional) - Owner-scoped API job ID.
- `data.poll_url` (string, optional) - Relative status URL.
- `data.result_url` (string, optional) - Relative result URL.
- `data.reused_active_job` (boolean, optional) - True only when this owner's active job was reused.
- `data.recommended_poll_after_ms` (number, optional) - Initial polling delay.

**Sample request**

```bash
curl -i -X POST https://startup-seeker.com/api/v2/general-enrichment-jobs \
+  -H "Authorization: Bearer ssk_live_your_api_key" \
+  -H "Idempotency-Key: crm-acme-2026-08-17" \
+  -H "Content-Type: application/json" \
+  --data '{"website":"https://acme.example"}'
```

**Sample response**

```json
{"data":{"job_id":"uuid","status":"queued","poll_url":"/api/v2/general-enrichment-jobs/uuid","result_url":"/api/v2/general-enrichment-jobs/uuid/result","reused_active_job":false,"recommended_poll_after_ms":2000},"meta":{"api_version":"v2","billing":{"currency":"USD","charged_usd":0.04,"remaining_usd":19.80}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.
- `Retry-After` - Recommended whole-second delay before the next status poll.
- `Location` - Relative status URL for the accepted job.

**Common errors**

- `400 BAD_REQUEST` - Request validation failed or contains an unknown field.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `402 INSUFFICIENT_FUNDS` - The account does not have enough USD balance to accept a new job.
- `409 IDEMPOTENCY_MISMATCH` - The idempotency key was already used with a different normalized payload.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.
- `404 NOT_FOUND` - The supplied webhook endpoint does not exist or is not owned by this account.

**Notes**

- Returns 202 Accepted and never waits for provider execution.
- Every customer job is stateless, including exact canonical-domain matches.

### List General enrichment jobs

- **Method:** `GET`
- **Path:** `/api/v2/general-enrichment-jobs`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{ "status": "running", "limit": 20, "after": "cursor" }
```

**Request fields**

- `status` (job status | null, optional) - Optional lifecycle filter.
- `limit` (integer | null, optional) - Page size from 1 to 100.
- `after` (string | null, optional) - Opaque pagination cursor.
- `request_id` (string | null, optional) - Optional request-ID filter.
- `idempotency_key` (string | null, optional) - Optional idempotency-key filter.

**Response fields**

- `data.jobs` (array, optional) - Owned job summaries with status and polling URLs.
- `data.next_cursor` (string | null, optional) - Cursor for the next page.

**Sample request**

```bash
curl -i 'https://startup-seeker.com/api/v2/general-enrichment-jobs?status=running&limit=20' -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{"data":{"jobs":[],"next_cursor":null},"meta":{"api_version":"v2","billing": {"currency": "USD","charged_usd":0,"remaining_usd": 19.80}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.

**Common errors**

- `400 BAD_REQUEST` - Request validation failed or contains an unknown field.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.

**Notes**

- Listing is rate-limited but free.

### Get General enrichment job

- **Method:** `GET`
- **Path:** `/api/v2/general-enrichment-jobs/{job_id}`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{ "job_id": "uuid" }
```

**Request fields**

- `job_id` (UUID, required) - Job ID returned by the start endpoint.

**Response fields**

- `data.status` (queued | running | succeeded | failed | cancelled, optional) - Current lifecycle state.
- `data.stage` (string | null, optional) - Bounded current stage, including waiting_for_entity.
- `data.recommended_poll_after_ms` (number | null, optional) - Server-selected 2s/5s/10s polling delay.
- `meta.job.recommended_poll_after_ms` (number | null, optional) - The same polling hint in standard job metadata.

**Sample request**

```bash
curl -i https://startup-seeker.com/api/v2/general-enrichment-jobs/JOB_ID -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{"data":{"job_id":"uuid","status":"running","stage":"funding_and_founders"},"meta":{"api_version":"v2","job":{"recommended_poll_after_ms":5000}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.
- `Retry-After` - Recommended whole-second delay before the next status poll.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.
- `404 NOT_FOUND` - Job does not exist or is not owned by this caller.

**Notes**

- Poll with the server hint plus jitter; persist the job ID across client restarts.

### Get General enrichment result

- **Method:** `GET`
- **Path:** `/api/v2/general-enrichment-jobs/{job_id}/result`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{ "job_id": "uuid" }
```

**Request fields**

- `job_id` (UUID, required) - Job ID returned by the start endpoint.

**Response fields**

- `data` (object, optional) - The typed stateless enrichment result itself; it is not wrapped in a second result property.
- `data.partial` (boolean | null, optional) - Combined jobs mark successful partial completion at the result root.

**Sample request**

```bash
curl -i https://startup-seeker.com/api/v2/general-enrichment-jobs/JOB_ID -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{"data":{"website":"https://acme.example","partial":false},"meta":{"api_version":"v2","billing": {"currency": "USD","charged_usd":0,"remaining_usd": 19.80}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.
- `Retry-After` - Recommended whole-second delay before the next status poll.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.
- `404 NOT_FOUND` - Job does not exist or is not owned by this caller.
- `409 JOB_NOT_COMPLETE` - The job is still queued or running.
- `410 JOB_RESULT_EXPIRED` - The 30-day result retention window has elapsed.
- `422 JOB_FAILED` - The terminal job failed or was cancelled; the message and optional stage are allowlisted.

**Notes**

- Terminal partial combined work returns success with partial=true and sanitized child errors.

### Start Founder enrichment

- **Method:** `POST`
- **Path:** `/api/v2/founder-enrichment-jobs`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account_with_balance`
- **USD price:** $0.10 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{
  "website": "https://acme.example",
  "company_name": "Acme",
  "webhook_endpoint_id": "owned-webhook-uuid"
}
```

**Request fields**

- `website` (string, required) - HTTP(S) website, at most 2,048 characters.
- `request_id` (string | null, optional) - Optional caller correlation ID, at most 128 characters.
- `company_name` (string | null, optional) - Optional bounded company-name hint.
- `description` (string | null, optional) - Optional description hint, at most 20,000 characters.
- `company_aliases` (string[] | null, optional) - At most 20 unique aliases, 200 characters each.
- `company_linkedin_url` (string | null, optional) - Optional LinkedIn company URL.
- `webhook_endpoint_id` (UUID | null, optional) - Optional webhook endpoint owned by this account.

**Response fields**

- `data.job_id` (UUID, optional) - Owner-scoped API job ID.
- `data.poll_url` (string, optional) - Relative status URL.
- `data.result_url` (string, optional) - Relative result URL.
- `data.reused_active_job` (boolean, optional) - True only when this owner's active job was reused.
- `data.recommended_poll_after_ms` (number, optional) - Initial polling delay.

**Sample request**

```bash
curl -i -X POST https://startup-seeker.com/api/v2/founder-enrichment-jobs \
+  -H "Authorization: Bearer ssk_live_your_api_key" \
+  -H "Idempotency-Key: crm-acme-2026-08-17" \
+  -H "Content-Type: application/json" \
+  --data '{"website":"https://acme.example"}'
```

**Sample response**

```json
{"data":{"job_id":"uuid","status":"queued","poll_url":"/api/v2/founder-enrichment-jobs/uuid","result_url":"/api/v2/founder-enrichment-jobs/uuid/result","reused_active_job":false,"recommended_poll_after_ms":2000},"meta":{"api_version":"v2","billing":{"currency":"USD","charged_usd":0.1,"remaining_usd":19.80}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.
- `Retry-After` - Recommended whole-second delay before the next status poll.
- `Location` - Relative status URL for the accepted job.

**Common errors**

- `400 BAD_REQUEST` - Request validation failed or contains an unknown field.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `402 INSUFFICIENT_FUNDS` - The account does not have enough USD balance to accept a new job.
- `409 IDEMPOTENCY_MISMATCH` - The idempotency key was already used with a different normalized payload.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.
- `404 NOT_FOUND` - The supplied webhook endpoint does not exist or is not owned by this account.

**Notes**

- Returns 202 Accepted and never waits for provider execution.
- Every customer job is stateless, including exact canonical-domain matches.

### List Founder enrichment jobs

- **Method:** `GET`
- **Path:** `/api/v2/founder-enrichment-jobs`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{ "status": "running", "limit": 20, "after": "cursor" }
```

**Request fields**

- `status` (job status | null, optional) - Optional lifecycle filter.
- `limit` (integer | null, optional) - Page size from 1 to 100.
- `after` (string | null, optional) - Opaque pagination cursor.
- `request_id` (string | null, optional) - Optional request-ID filter.
- `idempotency_key` (string | null, optional) - Optional idempotency-key filter.

**Response fields**

- `data.jobs` (array, optional) - Owned job summaries with status and polling URLs.
- `data.next_cursor` (string | null, optional) - Cursor for the next page.

**Sample request**

```bash
curl -i 'https://startup-seeker.com/api/v2/founder-enrichment-jobs?status=running&limit=20' -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{"data":{"jobs":[],"next_cursor":null},"meta":{"api_version":"v2","billing": {"currency": "USD","charged_usd":0,"remaining_usd": 19.80}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.

**Common errors**

- `400 BAD_REQUEST` - Request validation failed or contains an unknown field.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.

**Notes**

- Listing is rate-limited but free.

### Get Founder enrichment job

- **Method:** `GET`
- **Path:** `/api/v2/founder-enrichment-jobs/{job_id}`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{ "job_id": "uuid" }
```

**Request fields**

- `job_id` (UUID, required) - Job ID returned by the start endpoint.

**Response fields**

- `data.status` (queued | running | succeeded | failed | cancelled, optional) - Current lifecycle state.
- `data.stage` (string | null, optional) - Bounded current stage, including waiting_for_entity.
- `data.recommended_poll_after_ms` (number | null, optional) - Server-selected 2s/5s/10s polling delay.
- `meta.job.recommended_poll_after_ms` (number | null, optional) - The same polling hint in standard job metadata.

**Sample request**

```bash
curl -i https://startup-seeker.com/api/v2/founder-enrichment-jobs/JOB_ID -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{"data":{"job_id":"uuid","status":"running","stage":"funding_and_founders"},"meta":{"api_version":"v2","job":{"recommended_poll_after_ms":5000}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.
- `Retry-After` - Recommended whole-second delay before the next status poll.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.
- `404 NOT_FOUND` - Job does not exist or is not owned by this caller.

**Notes**

- Poll with the server hint plus jitter; persist the job ID across client restarts.

### Get Founder enrichment result

- **Method:** `GET`
- **Path:** `/api/v2/founder-enrichment-jobs/{job_id}/result`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{ "job_id": "uuid" }
```

**Request fields**

- `job_id` (UUID, required) - Job ID returned by the start endpoint.

**Response fields**

- `data` (object, optional) - The typed stateless enrichment result itself; it is not wrapped in a second result property.
- `data.partial` (boolean | null, optional) - Combined jobs mark successful partial completion at the result root.

**Sample request**

```bash
curl -i https://startup-seeker.com/api/v2/founder-enrichment-jobs/JOB_ID -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{"data":{"website":"https://acme.example","partial":false},"meta":{"api_version":"v2","billing": {"currency": "USD","charged_usd":0,"remaining_usd": 19.80}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.
- `Retry-After` - Recommended whole-second delay before the next status poll.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.
- `404 NOT_FOUND` - Job does not exist or is not owned by this caller.
- `409 JOB_NOT_COMPLETE` - The job is still queued or running.
- `410 JOB_RESULT_EXPIRED` - The 30-day result retention window has elapsed.
- `422 JOB_FAILED` - The terminal job failed or was cancelled; the message and optional stage are allowlisted.

**Notes**

- Terminal partial combined work returns success with partial=true and sanitized child errors.

### Start Funding enrichment

- **Method:** `POST`
- **Path:** `/api/v2/funding-enrichment-jobs`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account_with_balance`
- **USD price:** $0.10 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{
  "website": "https://acme.example",
  "company_name": "Acme",
  "webhook_endpoint_id": "owned-webhook-uuid"
}
```

**Request fields**

- `website` (string, required) - HTTP(S) website, at most 2,048 characters.
- `request_id` (string | null, optional) - Optional caller correlation ID, at most 128 characters.
- `company_name` (string | null, optional) - Optional bounded company-name hint.
- `description` (string | null, optional) - Optional description hint, at most 20,000 characters.
- `company_aliases` (string[] | null, optional) - At most 20 unique aliases, 200 characters each.
- `company_linkedin_url` (string | null, optional) - Optional LinkedIn company URL.
- `webhook_endpoint_id` (UUID | null, optional) - Optional webhook endpoint owned by this account.

**Response fields**

- `data.job_id` (UUID, optional) - Owner-scoped API job ID.
- `data.poll_url` (string, optional) - Relative status URL.
- `data.result_url` (string, optional) - Relative result URL.
- `data.reused_active_job` (boolean, optional) - True only when this owner's active job was reused.
- `data.recommended_poll_after_ms` (number, optional) - Initial polling delay.

**Sample request**

```bash
curl -i -X POST https://startup-seeker.com/api/v2/funding-enrichment-jobs \
+  -H "Authorization: Bearer ssk_live_your_api_key" \
+  -H "Idempotency-Key: crm-acme-2026-08-17" \
+  -H "Content-Type: application/json" \
+  --data '{"website":"https://acme.example"}'
```

**Sample response**

```json
{"data":{"job_id":"uuid","status":"queued","poll_url":"/api/v2/funding-enrichment-jobs/uuid","result_url":"/api/v2/funding-enrichment-jobs/uuid/result","reused_active_job":false,"recommended_poll_after_ms":2000},"meta":{"api_version":"v2","billing":{"currency":"USD","charged_usd":0.1,"remaining_usd":19.80}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.
- `Retry-After` - Recommended whole-second delay before the next status poll.
- `Location` - Relative status URL for the accepted job.

**Common errors**

- `400 BAD_REQUEST` - Request validation failed or contains an unknown field.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `402 INSUFFICIENT_FUNDS` - The account does not have enough USD balance to accept a new job.
- `409 IDEMPOTENCY_MISMATCH` - The idempotency key was already used with a different normalized payload.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.
- `404 NOT_FOUND` - The supplied webhook endpoint does not exist or is not owned by this account.

**Notes**

- Returns 202 Accepted and never waits for provider execution.
- Every customer job is stateless, including exact canonical-domain matches.

### List Funding enrichment jobs

- **Method:** `GET`
- **Path:** `/api/v2/funding-enrichment-jobs`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{ "status": "running", "limit": 20, "after": "cursor" }
```

**Request fields**

- `status` (job status | null, optional) - Optional lifecycle filter.
- `limit` (integer | null, optional) - Page size from 1 to 100.
- `after` (string | null, optional) - Opaque pagination cursor.
- `request_id` (string | null, optional) - Optional request-ID filter.
- `idempotency_key` (string | null, optional) - Optional idempotency-key filter.

**Response fields**

- `data.jobs` (array, optional) - Owned job summaries with status and polling URLs.
- `data.next_cursor` (string | null, optional) - Cursor for the next page.

**Sample request**

```bash
curl -i 'https://startup-seeker.com/api/v2/funding-enrichment-jobs?status=running&limit=20' -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{"data":{"jobs":[],"next_cursor":null},"meta":{"api_version":"v2","billing": {"currency": "USD","charged_usd":0,"remaining_usd": 19.80}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.

**Common errors**

- `400 BAD_REQUEST` - Request validation failed or contains an unknown field.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.

**Notes**

- Listing is rate-limited but free.

### Get Funding enrichment job

- **Method:** `GET`
- **Path:** `/api/v2/funding-enrichment-jobs/{job_id}`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{ "job_id": "uuid" }
```

**Request fields**

- `job_id` (UUID, required) - Job ID returned by the start endpoint.

**Response fields**

- `data.status` (queued | running | succeeded | failed | cancelled, optional) - Current lifecycle state.
- `data.stage` (string | null, optional) - Bounded current stage, including waiting_for_entity.
- `data.recommended_poll_after_ms` (number | null, optional) - Server-selected 2s/5s/10s polling delay.
- `meta.job.recommended_poll_after_ms` (number | null, optional) - The same polling hint in standard job metadata.

**Sample request**

```bash
curl -i https://startup-seeker.com/api/v2/funding-enrichment-jobs/JOB_ID -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{"data":{"job_id":"uuid","status":"running","stage":"funding_and_founders"},"meta":{"api_version":"v2","job":{"recommended_poll_after_ms":5000}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.
- `Retry-After` - Recommended whole-second delay before the next status poll.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.
- `404 NOT_FOUND` - Job does not exist or is not owned by this caller.

**Notes**

- Poll with the server hint plus jitter; persist the job ID across client restarts.

### Get Funding enrichment result

- **Method:** `GET`
- **Path:** `/api/v2/funding-enrichment-jobs/{job_id}/result`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{ "job_id": "uuid" }
```

**Request fields**

- `job_id` (UUID, required) - Job ID returned by the start endpoint.

**Response fields**

- `data` (object, optional) - The typed stateless enrichment result itself; it is not wrapped in a second result property.
- `data.partial` (boolean | null, optional) - Combined jobs mark successful partial completion at the result root.

**Sample request**

```bash
curl -i https://startup-seeker.com/api/v2/funding-enrichment-jobs/JOB_ID -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{"data":{"website":"https://acme.example","partial":false},"meta":{"api_version":"v2","billing": {"currency": "USD","charged_usd":0,"remaining_usd": 19.80}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.
- `Retry-After` - Recommended whole-second delay before the next status poll.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.
- `404 NOT_FOUND` - Job does not exist or is not owned by this caller.
- `409 JOB_NOT_COMPLETE` - The job is still queued or running.
- `410 JOB_RESULT_EXPIRED` - The 30-day result retention window has elapsed.
- `422 JOB_FAILED` - The terminal job failed or was cancelled; the message and optional stage are allowlisted.

**Notes**

- Terminal partial combined work returns success with partial=true and sanitized child errors.

### Start Combined enrichment

- **Method:** `POST`
- **Path:** `/api/v2/combined-enrichment-jobs`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account_with_balance`
- **USD price:** $0.20 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{
  "website": "https://acme.example",
  "company_name": "Acme",
  "funding": { "max_sources": 12 },
  "founders": { "enrich_linkedin": true }
}
```

**Request fields**

- `website` (string, required) - HTTP(S) website, at most 2,048 characters.
- `request_id` (string | null, optional) - Optional caller correlation ID, at most 128 characters.
- `company_name` (string | null, optional) - Optional bounded company-name hint.
- `description` (string | null, optional) - Optional description hint, at most 20,000 characters.
- `company_aliases` (string[] | null, optional) - At most 20 unique aliases, 200 characters each.
- `company_linkedin_url` (string | null, optional) - Optional LinkedIn company URL.
- `webhook_endpoint_id` (UUID | null, optional) - Optional webhook endpoint owned by this account.

**Response fields**

- `data.job_id` (UUID, optional) - Owner-scoped API job ID.
- `data.poll_url` (string, optional) - Relative status URL.
- `data.result_url` (string, optional) - Relative result URL.
- `data.reused_active_job` (boolean, optional) - True only when this owner's active job was reused.
- `data.recommended_poll_after_ms` (number, optional) - Initial polling delay.

**Sample request**

```bash
curl -i -X POST https://startup-seeker.com/api/v2/combined-enrichment-jobs \
+  -H "Authorization: Bearer ssk_live_your_api_key" \
+  -H "Idempotency-Key: crm-acme-2026-08-17" \
+  -H "Content-Type: application/json" \
+  --data '{"website":"https://acme.example"}'
```

**Sample response**

```json
{"data":{"job_id":"uuid","status":"queued","poll_url":"/api/v2/combined-enrichment-jobs/uuid","result_url":"/api/v2/combined-enrichment-jobs/uuid/result","reused_active_job":false,"recommended_poll_after_ms":2000},"meta":{"api_version":"v2","billing":{"currency":"USD","charged_usd":0.2,"remaining_usd":19.80}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.
- `Retry-After` - Recommended whole-second delay before the next status poll.
- `Location` - Relative status URL for the accepted job.

**Common errors**

- `400 BAD_REQUEST` - Request validation failed or contains an unknown field.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `402 INSUFFICIENT_FUNDS` - The account does not have enough USD balance to accept a new job.
- `409 IDEMPOTENCY_MISMATCH` - The idempotency key was already used with a different normalized payload.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.
- `404 NOT_FOUND` - The supplied webhook endpoint does not exist or is not owned by this account.

**Notes**

- Returns 202 Accepted and never waits for provider execution.
- Every customer job is stateless, including exact canonical-domain matches.
- Combined always runs general, funding, and founders; child switches are rejected.

### List Combined enrichment jobs

- **Method:** `GET`
- **Path:** `/api/v2/combined-enrichment-jobs`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{ "status": "running", "limit": 20, "after": "cursor" }
```

**Request fields**

- `status` (job status | null, optional) - Optional lifecycle filter.
- `limit` (integer | null, optional) - Page size from 1 to 100.
- `after` (string | null, optional) - Opaque pagination cursor.
- `request_id` (string | null, optional) - Optional request-ID filter.
- `idempotency_key` (string | null, optional) - Optional idempotency-key filter.

**Response fields**

- `data.jobs` (array, optional) - Owned job summaries with status and polling URLs.
- `data.next_cursor` (string | null, optional) - Cursor for the next page.

**Sample request**

```bash
curl -i 'https://startup-seeker.com/api/v2/combined-enrichment-jobs?status=running&limit=20' -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{"data":{"jobs":[],"next_cursor":null},"meta":{"api_version":"v2","billing": {"currency": "USD","charged_usd":0,"remaining_usd": 19.80}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.

**Common errors**

- `400 BAD_REQUEST` - Request validation failed or contains an unknown field.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.

**Notes**

- Listing is rate-limited but free.

### Get Combined enrichment job

- **Method:** `GET`
- **Path:** `/api/v2/combined-enrichment-jobs/{job_id}`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{ "job_id": "uuid" }
```

**Request fields**

- `job_id` (UUID, required) - Job ID returned by the start endpoint.

**Response fields**

- `data.status` (queued | running | succeeded | failed | cancelled, optional) - Current lifecycle state.
- `data.stage` (string | null, optional) - Bounded current stage, including waiting_for_entity.
- `data.recommended_poll_after_ms` (number | null, optional) - Server-selected 2s/5s/10s polling delay.
- `meta.job.recommended_poll_after_ms` (number | null, optional) - The same polling hint in standard job metadata.

**Sample request**

```bash
curl -i https://startup-seeker.com/api/v2/combined-enrichment-jobs/JOB_ID -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{"data":{"job_id":"uuid","status":"running","stage":"funding_and_founders"},"meta":{"api_version":"v2","job":{"recommended_poll_after_ms":5000}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.
- `Retry-After` - Recommended whole-second delay before the next status poll.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.
- `404 NOT_FOUND` - Job does not exist or is not owned by this caller.

**Notes**

- Poll with the server hint plus jitter; persist the job ID across client restarts.

### Get Combined enrichment result

- **Method:** `GET`
- **Path:** `/api/v2/combined-enrichment-jobs/{job_id}/result`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{ "job_id": "uuid" }
```

**Request fields**

- `job_id` (UUID, required) - Job ID returned by the start endpoint.

**Response fields**

- `data` (object, optional) - The typed stateless enrichment result itself; it is not wrapped in a second result property.
- `data.partial` (boolean | null, optional) - Combined jobs mark successful partial completion at the result root.

**Sample request**

```bash
curl -i https://startup-seeker.com/api/v2/combined-enrichment-jobs/JOB_ID -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{"data":{"website":"https://acme.example","partial":true},"meta":{"api_version":"v2","billing": {"currency": "USD","charged_usd":0,"remaining_usd": 19.80}}}
```

**Important headers**

- `Authorization` - Required API key bearer token.
- `X-Balance-Remaining-USD` - USD balance remaining after request settlement.
- `Retry-After` - Recommended whole-second delay before the next status poll.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - API-key or account rate limit exceeded.
- `404 NOT_FOUND` - Job does not exist or is not owned by this caller.
- `409 JOB_NOT_COMPLETE` - The job is still queued or running.
- `410 JOB_RESULT_EXPIRED` - The 30-day result retention window has elapsed.
- `422 JOB_FAILED` - The terminal job failed or was cancelled; the message and optional stage are allowlisted.

**Notes**

- Terminal partial combined work returns success with partial=true and sanitized child errors.

### Competitor Analyses

- **Method:** `POST`
- **Path:** `/api/v2/competitor-analyses`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account_with_balance`
- **USD price:** $0.02 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{
  "request_id": "competitor-v2-demo-1",
  "company": {
    "name": "Acme Analytics",
    "website": "acmeanalytics.com"
  }
}
```

**Request fields**

- `request_id` (string | null, optional) - Optional client correlation ID. Maximum 128 characters.
- `company.name` (string | null, optional) - Optional company name used to improve resolution.
- `company.website` (string | null, optional) - Optional website. Provide this whenever possible.
- `company.description` (string | null, optional) - Optional description. Required when no website is provided.

**Response fields**

- `data.company` (object, optional) - Contains the input payload and resolved company context.
- `data.competitors` (array<object>, optional) - Competitor matches and AI-generated summaries.
- `data.competitors[].similarity_level` (SAME_COMPANY | NOT RELEVANT | LOW | MEDIUM | HIGH | VERY HIGH, optional) - Categorical product, buyer, and market similarity.
- `data.competitors[].competitor_type` (DIRECT | INDIRECT | null, optional) - Whether the candidate is a close product substitute or competes for the same job through a different approach.
- `data.total_analyzed` (number, optional) - Number of competitor records returned.
- `data.search_query` (string | null, optional) - Search query generated by the competitor analysis service.
- `meta` (object, optional) - Standard v2 metadata envelope with rate-limit and balance state.

**Sample request**

```bash
curl -X POST https://startup-seeker.com/api/v2/competitor-analyses \
  -H "Authorization: Bearer ssk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "company": {
      "name": "Acme Analytics",
      "website": "acmeanalytics.com"
    }
  }'
```

**Sample response**

```json
{
  "data": {
    "company": {
      "input": {
        "name": "Acme Analytics",
        "website": "acmeanalytics.com",
        "description": null
      },
      "resolved": {
        "name": "Acme Analytics",
        "normalized_website": "acmeanalytics.com"
      }
    },
    "competitors": [
      {
        "company": {
          "name": "DataViz Pro",
          "website": "datavizpro.io"
        },
        "similarity_level": "HIGH",
        "competitor_type": "DIRECT",
        "summary": "DataViz Pro provides analytics workflow software for data teams. It is a direct competitor with the same core job, but its product focuses more narrowly on visualization workflows."
      }
    ],
    "total_analyzed": 1,
    "search_query": "analytics workflow competitors"
  },
  "meta": {
    "request_id": "competitor-v2-demo-1",
    "api_version": "v2",
    "duration_ms": 920,
    "billing": {"currency": "USD", "charged_usd": 0.02, "remaining_usd": 19.98 }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `X-RateLimit-Limit-Minute` - Effective per-minute limit for the key/account.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `400 BAD_REQUEST` - Invalid JSON or missing both `company.website` and `company.description`.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.
- `500 INTERNAL_ERROR` - Competitor analysis failed.

**Notes**

- Legacy v1 competitors has been removed. Use this v2 route for public competitor analysis integrations.
- Provide `company.website` whenever possible for the most stable resolution path.

### VC Backability Checks

- **Method:** `POST`
- **Path:** `/api/v2/vc-backability-checks`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account_with_balance`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{
  "request_id": "vc-v2-demo-1",
  "websites": ["runwayml.com", "genei.io"],
  "response_detail": "minimal"
}
```

**Request fields**

- `request_id` (string | null, optional) - Optional client correlation ID. Maximum 128 characters.
- `websites` (array<string> | null, optional) - Preferred batch input. Supply 1-50 websites for a sync response, or 51-500 to create an async job.
- `website` (string | null, optional) - Single-site shortcut when not using `websites`.
- `response_detail` ("minimal" | "descriptive" | "full" | null, optional) - Defaults to `minimal`. Minimal responses omit company descriptions. `descriptive` adds the short company description. `full` adds name, cache metadata, and queue/existing flags. Raw homepage scrape content is never exposed.

**Response fields**

- `data.count` (number, optional) - Number of entries returned in `data.results`.
- `data.results` (array<object>, optional) - One item per submitted website, preserving order. Row failures are returned inline with `status: "error"`.
- `data.summary` (object, optional) - Counters for row errors, canonical hits, active queue hits, cache hits, and live classifications.
- `meta` (object, optional) - Standard v2 metadata envelope with rate-limit and balance state.

**Sample request**

```bash
curl -X POST https://startup-seeker.com/api/v2/vc-backability-checks \
  -H "Authorization: Bearer ssk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "websites": ["runwayml.com", "genei.io"],
    "response_detail": "minimal"
  }'
```

**Sample response**

```json
{
  "data": {
    "count": 2,
    "results": [
      {
        "input": "runwayml.com",
        "website": "runwayml.com",
        "status": "classified",
        "classification": "Yes",
        "source": "canonical_startup",
        "error": null
      },
      {
        "input": "genei.io",
        "website": "genei.io",
        "status": "classified",
        "classification": "Likely Not",
        "source": "live_classifier",
        "error": null
      }
    ],
    "summary": {
      "classified": 2,
      "errors": 0,
      "canonical_hits": 1,
      "active_queue_hits": 0,
      "cache_hits": 0,
      "live_classifications": 1
    }
  },
  "meta": {
    "request_id": "vc-v2-demo-1",
    "api_version": "v2",
    "duration_ms": 2140,
    "billing": {"currency": "USD", "charged_usd": 0, "remaining_usd": 19.98 }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `Idempotency-Key` - Optional for async requests. Reusing the same key with the same payload returns the existing job.
- `X-RateLimit-Limit-Minute` - Effective per-minute limit for the key/account.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `400 BAD_REQUEST` - Request omitted both `website` and `websites`, exceeded 500 websites, or validation failed.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `409 IDEMPOTENCY_MISMATCH` - The async idempotency key was reused with a different payload.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.
- `500 INTERNAL_ERROR` - Systemic API/job failure. Provider failures for individual websites are returned as row errors.

**Notes**

- Legacy v1 VC backability classifier routes have been removed. Use this v2 route for public VC backability checks.
- Canonical StartupSeeker matches always return `Yes` without scraping. Active post-gate startup enrichment queue matches also return `Yes` and can be reused by upload flows.
- For 51-500 websites, the POST returns `202` with `poll_url` and `result_url`. Poll `GET /api/v2/vc-backability-checks/{job_id}` and fetch `GET /api/v2/vc-backability-checks/{job_id}/result` when complete.

### List VC Backability Jobs

- **Method:** `GET`
- **Path:** `/api/v2/vc-backability-checks`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
GET /api/v2/vc-backability-checks?status=running&limit=10&request_id=vc-v2-demo-1
```

**Request fields**

- `status` (queued | running | succeeded | failed | cancelled, optional) - Optional status filter.
- `limit` (integer, optional) - Optional page size from 1 to 100. Defaults to `20`.
- `after` (string, optional) - Optional cursor returned as `data.next_cursor` for pagination.
- `request_id` (string, optional) - Optional exact-match filter on the original request ID.
- `idempotency_key` (string, optional) - Optional exact-match filter on the original idempotency key.

**Response fields**

- `data.jobs[].job_id` (string, optional) - Job identifier.
- `data.jobs[].request_id` (string | null, optional) - Original client request ID, when supplied.
- `data.jobs[].status` (string, optional) - Current job lifecycle state.
- `data.jobs[].progress` (number, optional) - Coarse job progress percentage.
- `data.jobs[].poll_url` (string, optional) - Relative path for polling job status. Prefix it with your API base URL.
- `data.jobs[].result_url` (string, optional) - Relative path for retrieving the completed result. Prefix it with your API base URL.
- `data.next_cursor` (string | null, optional) - Opaque cursor for the next page, or `null` when exhausted.
- `meta` (object, optional) - Standard v2 metadata envelope.

**Sample request**

```bash
curl "https://startup-seeker.com/api/v2/vc-backability-checks?status=running&limit=10&request_id=vc-v2-demo-1" \
  -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{
  "data": {
    "jobs": [
      {
        "job_id": "58466a0d-4a9a-41b2-8753-5d30749c5d4d",
        "request_id": "vc-v2-demo-1",
        "status": "running",
        "progress": 45,
        "created_at": "2026-04-26T01:27:58.625Z",
        "updated_at": "2026-04-26T01:28:03.081Z",
        "completed_at": null,
        "poll_url": "/api/v2/vc-backability-checks/58466a0d-4a9a-41b2-8753-5d30749c5d4d",
        "result_url": "/api/v2/vc-backability-checks/58466a0d-4a9a-41b2-8753-5d30749c5d4d/result"
      }
    ],
    "next_cursor": null
  },
  "meta": {
    "request_id": "4a89ad5a-5d85-4fb6-8cb5-2c8df3b7a4df",
    "api_version": "v2",
    "duration_ms": 8,
    "billing": {"currency": "USD", "charged_usd": 0, "remaining_usd": 19.98 }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `400 BAD_REQUEST` - Query parameter validation failed.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.
- `500 INTERNAL_ERROR` - Unexpected job listing failure.

**Notes**

- Use this route to recover job IDs if the original 202 response was not persisted client-side.
- Only VC backability jobs owned by the authenticated API key are returned.
- `poll_url` and `result_url` are relative paths, not absolute URLs.

### VC Backability Job Status

- **Method:** `GET`
- **Path:** `/api/v2/vc-backability-checks/{job_id}`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
GET /api/v2/vc-backability-checks/{job_id}
```

**Request fields**

- `job_id` (string, required) - Job identifier returned by the async 202 response or list endpoint.

**Response fields**

- `data.job_id` (string, optional) - Job identifier.
- `data.job_type` (string, optional) - Job type, currently `vc_backability_check`.
- `data.status` (string, optional) - Job lifecycle state: `queued`, `running`, `succeeded`, or `failed`.
- `data.progress` (number, optional) - Coarse job progress percentage.
- `data.response_meta` (object, optional) - Counters such as website count, row errors, cache hits, and live classifications.
- `meta.job` (object, optional) - Job metadata mirrored into the standard v2 meta envelope.

**Sample request**

```bash
curl https://startup-seeker.com/api/v2/vc-backability-checks/58466a0d-4a9a-41b2-8753-5d30749c5d4d \
  -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{
  "data": {
    "job_id": "58466a0d-4a9a-41b2-8753-5d30749c5d4d",
    "job_type": "vc_backability_check",
    "status": "running",
    "progress": 45,
    "created_at": "2026-04-26T01:27:58.625Z",
    "updated_at": "2026-04-26T01:28:03.081Z",
    "completed_at": null,
    "response_meta": {
      "endpoint": "/api/v2/vc-backability-checks",
      "website_count": 125,
      "status": "running"
    }
  },
  "meta": {
    "request_id": "2db6b347-f88c-4bb7-92c9-09f0b8d8d74b",
    "api_version": "v2",
    "duration_ms": 9,
    "billing": {"currency": "USD", "charged_usd": 0, "remaining_usd": 19.98 },
    "job": {
      "id": "58466a0d-4a9a-41b2-8753-5d30749c5d4d",
      "status": "running"
    }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `404 NOT_FOUND` - Job does not exist or is not owned by the requesting API key.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.
- `500 INTERNAL_ERROR` - Unexpected job lookup failure.

**Notes**

- Status polling is rate-limited but not balance-charged.
- When `data.status` becomes `succeeded`, call `GET /api/v2/vc-backability-checks/{job_id}/result` for the same job ID.

### VC Backability Job Result

- **Method:** `GET`
- **Path:** `/api/v2/vc-backability-checks/{job_id}/result`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
GET /api/v2/vc-backability-checks/{job_id}/result
```

**Request fields**

- `job_id` (string, required) - Job identifier returned by the async 202 response or list endpoint.

**Response fields**

- `data.job_id` (string, optional) - Job identifier.
- `data.status` (string, optional) - Final job state, typically `succeeded`.
- `data.result.count` (number, optional) - Number of submitted entries.
- `data.result.results` (array<object>, optional) - One VC backability row per submitted website, preserving order.
- `data.result.summary` (object, optional) - Counters for classified rows, row errors, canonical hits, active queue hits, cache hits, and live classifications.
- `meta` (object, optional) - Standard v2 metadata envelope.

**Sample request**

```bash
curl https://startup-seeker.com/api/v2/vc-backability-checks/58466a0d-4a9a-41b2-8753-5d30749c5d4d/result \
  -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{
  "data": {
    "job_id": "58466a0d-4a9a-41b2-8753-5d30749c5d4d",
    "status": "succeeded",
    "result": {
      "count": 1,
      "results": [
        {
          "input": "runwayml.com",
          "website": "runwayml.com",
          "status": "classified",
          "classification": "Yes",
          "source": "canonical_startup",
          "error": null
        }
      ],
      "summary": {
        "classified": 1,
        "errors": 0,
        "canonical_hits": 1,
        "active_queue_hits": 0,
        "cache_hits": 0,
        "live_classifications": 0
      }
    }
  },
  "meta": {
    "request_id": "34db6728-f1af-4f9a-bc78-1d3a0b4e5d44",
    "api_version": "v2",
    "duration_ms": 7,
    "billing": {"currency": "USD", "charged_usd": 0, "remaining_usd": 19.98 },
    "job": {
      "id": "58466a0d-4a9a-41b2-8753-5d30749c5d4d",
      "status": "succeeded"
    }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `404 NOT_FOUND` - Job does not exist or is not owned by the requesting API key.
- `409 JOB_NOT_READY` - Job exists but has not completed yet.
- `422 JOB_FAILED` - Job reached a failed terminal state; inspect `error.details`.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.

**Notes**

- The final result endpoint is rate-limited but not balance-charged.
- The completed result uses the same `data.count`, `data.results`, and `data.summary` shape as the synchronous 200 response, nested under `data.result`.
- Raw homepage scrape content is never exposed in async results.

### List Pitch Deck Jobs

- **Method:** `GET`
- **Path:** `/api/v2/pitch-deck-jobs`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
GET /api/v2/pitch-deck-jobs?status=running&limit=10&request_id=pitch-deck-demo-1
```

**Request fields**

- `status` (queued | running | succeeded | failed | cancelled, optional) - Optional status filter.
- `limit` (integer, optional) - Optional page size from 1 to 100. Defaults to `20`.
- `after` (string, optional) - Optional cursor returned as `data.next_cursor` for pagination.
- `request_id` (string, optional) - Optional exact-match filter on the original request ID.
- `idempotency_key` (string, optional) - Optional exact-match filter on the original idempotency key.

**Response fields**

- `data.jobs[].job_id` (string, optional) - Job identifier.
- `data.jobs[].request_id` (string | null, optional) - Original client request ID, when supplied.
- `data.jobs[].status` (string, optional) - Current job lifecycle state.
- `data.jobs[].created_at / updated_at / completed_at` (string | null, optional) - Lifecycle timestamps for the job.
- `data.jobs[].poll_url` (string, optional) - Relative path for polling job status. Prefix it with your API base URL.
- `data.jobs[].result_url` (string, optional) - Relative path for retrieving the completed result. Prefix it with your API base URL.
- `data.next_cursor` (string | null, optional) - Opaque cursor for the next page, or `null` when exhausted.
- `meta` (object, optional) - Standard v2 metadata envelope.

**Sample request**

```bash
curl "https://startup-seeker.com/api/v2/pitch-deck-jobs?status=running&limit=10&request_id=pitch-deck-demo-1" \
  -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{
  "data": {
    "jobs": [
      {
        "job_id": "3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4",
        "request_id": "pitch-deck-demo-1",
        "status": "running",
        "created_at": "2026-03-14T12:00:00.000Z",
        "updated_at": "2026-03-14T12:00:03.000Z",
        "completed_at": null,
        "poll_url": "/api/v2/pitch-deck-jobs/3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4",
        "result_url": "/api/v2/pitch-deck-jobs/3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4/result"
      }
    ],
    "next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0wMy0xNFQxMjowMDowMy4wMDBaIiwiaWQiOiIzYzZhNjE3Ni0xZjhkLTRlOGQtYjFhMC05YmY5ZDRiNmU3ZDQifQ"
  },
  "meta": {
    "request_id": "8bc6b4b1-0b41-4c1b-b1d8-f1cb11bd1af7",
    "api_version": "v2",
    "duration_ms": 5,
    "billing": {"currency": "USD", "charged_usd": 0, "remaining_usd": 19.90 }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `400 BAD_REQUEST` - Query parameter validation failed.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.
- `500 INTERNAL_ERROR` - Unexpected job listing failure.

**Notes**

- Use this route to recover job IDs if the original create response was not persisted client-side.
- Only jobs owned by the authenticated API key are returned.
- Use `data.next_cursor` as the `after` parameter to fetch the next page.
- `poll_url` and `result_url` are relative paths, not absolute URLs.

### Create Pitch Deck Job

- **Method:** `POST`
- **Path:** `/api/v2/pitch-deck-jobs`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account_with_balance`
- **USD price:** $0.10 per request.
- **Content-Type:** `multipart/form-data`

**Request shape**

```
multipart/form-data
- file: <PDF, required>
- request_id: <string, optional>
- webhook_endpoint_id: <uuid, optional>
```

**Request fields**

- `file` (File, required) - Pitch deck PDF. Maximum 30 MB.
- `request_id` (string | null, optional) - Optional client correlation ID.
- `webhook_endpoint_id` (uuid | null, optional) - Optional managed webhook endpoint that receives job completion events.
- `Idempotency-Key` (header, optional) - Optional idempotency key. Reusing the same key for the same API key with the same payload returns the existing job instead of charging again.

**Response fields**

- `data.job_id` (string, optional) - Created or reused job identifier.
- `data.status` (string, optional) - Initial job status, typically `queued`.
- `data.poll_url` (string, optional) - Relative path for polling job status. Prefix it with your API base URL.
- `data.result_url` (string, optional) - Relative path for retrieving the completed result. Prefix it with your API base URL.
- `meta.job` (object, optional) - Job metadata mirrored into the standard v2 meta envelope.

**Sample request**

```bash
curl -X POST https://startup-seeker.com/api/v2/pitch-deck-jobs \
  -H "Authorization: Bearer ssk_live_your_api_key" \
  -H "Idempotency-Key: pitch-deck-demo-1" \
  -F "file=@deck.pdf" \
  -F "request_id=pitch-deck-demo-1" \
  -F "webhook_endpoint_id=7c0d1e8b-302d-4d03-b38f-1ce85fa7999d"
```

**Sample response**

```json
{
  "data": {
    "job_id": "3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4",
    "status": "queued",
    "poll_url": "/api/v2/pitch-deck-jobs/3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4",
    "result_url": "/api/v2/pitch-deck-jobs/3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4/result"
  },
  "meta": {
    "request_id": "pitch-deck-demo-1",
    "api_version": "v2",
    "duration_ms": 45,
    "billing": {"currency": "USD", "charged_usd": 0.10, "remaining_usd": 19.90 },
    "job": {
      "id": "3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4",
      "status": "queued"
    }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `Idempotency-Key` - Optional retry-deduplication key. Replays only succeed when the payload matches the original request.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `400 BAD_REQUEST` - Request was not multipart/form-data or no PDF was supplied.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `402 INSUFFICIENT_FUNDS` - Account has insufficient USD balance.
- `409 IDEMPOTENCY_MISMATCH` - The same idempotency key was reused with a different payload.
- `413 FILE_TOO_LARGE` - Uploaded PDF exceeds the 30 MB limit.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.
- `500 INTERNAL_ERROR` - Unexpected job creation failure.

**Notes**

- Legacy v1 parse-deck has been removed. Use this v2 async job route for public pitch-deck parsing integrations.
- The upload returns `202 Accepted`; fetch the final deck output from the result endpoint.
- If the same `Idempotency-Key` is replayed for the same API key with the same payload, StartupSeeker returns the existing job without charging the USD balance again.
- If the same idempotency key is reused with a different payload, the API returns `409 IDEMPOTENCY_MISMATCH`.
- If `webhook_endpoint_id` is supplied, StartupSeeker posts signed `api.job.succeeded` or `api.job.failed` events to that endpoint.

### Pitch Deck Job Status

- **Method:** `GET`
- **Path:** `/api/v2/pitch-deck-jobs/{job_id}`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
GET /api/v2/pitch-deck-jobs/{job_id}
```

**Request fields**

- `job_id` (string, required) - Job identifier returned by the job creation endpoint.

**Response fields**

- `data.job_id` (string, optional) - Job identifier.
- `data.job_type` (string, optional) - Job type, currently `pitch_deck`.
- `data.status` (string, optional) - Job lifecycle state: `queued`, `running`, `succeeded`, or `failed`.
- `data.progress` (number, optional) - Coarse job progress percentage.
- `data.created_at / updated_at / completed_at` (string | null, optional) - Lifecycle timestamps for the job.
- `meta.job` (object, optional) - Job metadata mirrored into the standard v2 meta envelope.

**Sample request**

```bash
curl https://startup-seeker.com/api/v2/pitch-deck-jobs/3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4 \
  -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{
  "data": {
    "job_id": "3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4",
    "job_type": "pitch_deck",
    "status": "running",
    "progress": 10,
    "created_at": "2026-03-14T12:00:00.000Z",
    "updated_at": "2026-03-14T12:00:03.000Z",
    "completed_at": null
  },
  "meta": {
    "request_id": "2db6b347-f88c-4bb7-92c9-09f0b8d8d74b",
    "api_version": "v2",
    "duration_ms": 9,
    "billing": {"currency": "USD", "charged_usd": 0, "remaining_usd": 19.90 },
    "job": {
      "id": "3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4",
      "status": "running"
    }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `404 NOT_FOUND` - Job does not exist or is not owned by the requesting API key.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.
- `500 INTERNAL_ERROR` - Unexpected job lookup failure.

**Notes**

- Status polling is rate-limited but not balance-charged.
- Ownership is enforced per API key; one key cannot inspect another key’s jobs.

### Pitch Deck Job Result

- **Method:** `GET`
- **Path:** `/api/v2/pitch-deck-jobs/{job_id}/result`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
GET /api/v2/pitch-deck-jobs/{job_id}/result
```

**Request fields**

- `job_id` (string, required) - Job identifier returned by the job creation endpoint.

**Response fields**

- `data.job_id` (string, optional) - Job identifier.
- `data.status` (string, optional) - Final job state, typically `succeeded`.
- `data.result` (object, optional) - Pitch-deck parsing result with slide summaries and markdown.
- `meta` (object, optional) - Standard v2 metadata envelope.

**Sample request**

```bash
curl https://startup-seeker.com/api/v2/pitch-deck-jobs/3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4/result \
  -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{
  "data": {
    "job_id": "3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4",
    "status": "succeeded",
    "result": {
      "request_id": "pitch-deck-demo-1",
      "filename": "deck.pdf",
      "total_pages": 12,
      "processed_slides": 12,
      "slides": [],
      "markdown": "## Slide 1..."
    }
  },
  "meta": {
    "request_id": "34db6728-f1af-4f9a-bc78-1d3a0b4e5d44",
    "api_version": "v2",
    "duration_ms": 7,
    "billing": {"currency": "USD", "charged_usd": 0, "remaining_usd": 19.90 },
    "job": {
      "id": "3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4",
      "status": "succeeded"
    }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `404 NOT_FOUND` - Job does not exist or is not owned by the requesting API key.
- `409 JOB_NOT_READY` - Job exists but has not completed yet.
- `422 JOB_FAILED` - Job reached a failed terminal state; inspect `error.details`.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.

**Notes**

- The final result endpoint is rate-limited but not balance-charged.
- Failed jobs return the standard v2 error envelope instead of a success body.

### List Deep Research Jobs

- **Method:** `GET`
- **Path:** `/api/v2/deep-research-jobs`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
GET /api/v2/deep-research-jobs?status=succeeded&limit=10&request_id=deep-research-v2-demo-1
```

**Request fields**

- `status` (queued | running | succeeded | failed | cancelled, optional) - Optional status filter.
- `limit` (integer, optional) - Optional page size from 1 to 100. Defaults to `20`.
- `after` (string, optional) - Optional cursor returned as `data.next_cursor` for pagination.
- `request_id` (string, optional) - Optional exact-match filter on the original request ID.
- `idempotency_key` (string, optional) - Optional exact-match filter on the original idempotency key.

**Response fields**

- `data.jobs[].job_id` (string, optional) - Job identifier.
- `data.jobs[].request_id` (string | null, optional) - Original client request ID, when supplied.
- `data.jobs[].status` (string, optional) - Current job lifecycle state.
- `data.jobs[].created_at / updated_at / completed_at` (string | null, optional) - Lifecycle timestamps for the job.
- `data.jobs[].poll_url` (string, optional) - Relative path for polling job status. Prefix it with your API base URL.
- `data.jobs[].result_url` (string, optional) - Relative path for retrieving the completed result. Prefix it with your API base URL.
- `data.next_cursor` (string | null, optional) - Opaque cursor for the next page, or `null` when exhausted.
- `meta` (object, optional) - Standard v2 metadata envelope.

**Sample request**

```bash
curl "https://startup-seeker.com/api/v2/deep-research-jobs?status=succeeded&limit=10&request_id=deep-research-v2-demo-1" \
  -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{
  "data": {
    "jobs": [
      {
        "job_id": "d61d9b95-06f8-47a8-a409-77d9ee5756d1",
        "request_id": "deep-research-v2-demo-1",
        "status": "succeeded",
        "created_at": "2026-03-14T12:10:00.000Z",
        "updated_at": "2026-03-14T12:10:34.000Z",
        "completed_at": "2026-03-14T12:10:34.000Z",
        "poll_url": "/api/v2/deep-research-jobs/d61d9b95-06f8-47a8-a409-77d9ee5756d1",
        "result_url": "/api/v2/deep-research-jobs/d61d9b95-06f8-47a8-a409-77d9ee5756d1/result"
      }
    ],
    "next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0wMy0xNFQxMjoxMDozNC4wMDBaIiwiaWQiOiJkNjFkOWI5NS0wNmY4LTQ3YTgtYTQwOS03N2Q5ZWU1NzU2ZDEifQ"
  },
  "meta": {
    "request_id": "ab34fd45-c9fc-4f47-b1de-8db71b3d3ff6",
    "api_version": "v2",
    "duration_ms": 4,
    "billing": {"currency": "USD", "charged_usd": 0, "remaining_usd": 19.80 }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `400 BAD_REQUEST` - Query parameter validation failed.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.
- `500 INTERNAL_ERROR` - Unexpected job listing failure.

**Notes**

- Use this route to recover job IDs if the original create response was not persisted client-side.
- Only jobs owned by the authenticated API key are returned.
- Use `data.next_cursor` as the `after` parameter to fetch the next page.
- `poll_url` and `result_url` are relative paths, not absolute URLs.

### Create Deep Research Job

- **Method:** `POST`
- **Path:** `/api/v2/deep-research-jobs`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account_with_balance`
- **USD price:** $0.20 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{
  "request_id": "deep-research-v2-demo-1",
  "website": "exa.ai",
  "company_name": "Exa"
}
```

**Request fields**

- `request_id` (string | null, optional) - Optional client correlation ID. Maximum 128 characters.
- `website` (string, required) - Website/domain to research.
- `zilliz_id` (string | null, optional) - Optional existing StartupSeeker record ID.
- `city / country / country_iso` (string | null, optional) - Optional location overrides used during the workflow. Use ISO-2 codes such as `US` or `DE` for `country_iso`.
- `company_name` (string | null, optional) - Optional company name override.
- `webhook_endpoint_id` (uuid | null, optional) - Optional managed webhook endpoint that receives job completion events.
- `Idempotency-Key` (header, optional) - Optional idempotency key. Reusing the same key for the same API key with the same payload returns the existing job instead of charging again.

**Response fields**

- `data.job_id` (string, optional) - Created or reused job identifier.
- `data.status` (string, optional) - Initial job status, typically `queued`.
- `data.poll_url` (string, optional) - Relative path for polling job status. Prefix it with your API base URL.
- `data.result_url` (string, optional) - Relative path for retrieving the completed result. Prefix it with your API base URL.
- `meta.job` (object, optional) - Job metadata mirrored into the standard v2 meta envelope.

**Sample request**

```bash
curl -X POST https://startup-seeker.com/api/v2/deep-research-jobs \
  -H "Authorization: Bearer ssk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: deep-research-demo-1" \
  -d '{
    "request_id": "deep-research-v2-demo-1",
    "website": "exa.ai",
    "company_name": "Exa",
    "webhook_endpoint_id": "7c0d1e8b-302d-4d03-b38f-1ce85fa7999d"
  }'
```

**Sample response**

```json
{
  "data": {
    "job_id": "d61d9b95-06f8-47a8-a409-77d9ee5756d1",
    "status": "queued",
    "poll_url": "/api/v2/deep-research-jobs/d61d9b95-06f8-47a8-a409-77d9ee5756d1",
    "result_url": "/api/v2/deep-research-jobs/d61d9b95-06f8-47a8-a409-77d9ee5756d1/result"
  },
  "meta": {
    "request_id": "deep-research-v2-demo-1",
    "api_version": "v2",
    "duration_ms": 38,
    "billing": {"currency": "USD", "charged_usd": 0.20, "remaining_usd": 19.80 },
    "job": {
      "id": "d61d9b95-06f8-47a8-a409-77d9ee5756d1",
      "status": "queued"
    }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `Idempotency-Key` - Optional retry-deduplication key. Replays only succeed when the payload matches the original request.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `400 BAD_REQUEST` - Invalid JSON, missing `website`, or schema validation failed.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `402 INSUFFICIENT_FUNDS` - Account has insufficient USD balance.
- `409 IDEMPOTENCY_MISMATCH` - The same idempotency key was reused with a different payload.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.
- `500 INTERNAL_ERROR` - Unexpected job creation failure.

**Notes**

- Public API access is API-key-only in v2.
- The legacy synchronous `/api/deep-research` route has been removed and now returns `410 Gone` with `/api/v2/deep-research-jobs` as the replacement.
- If the same `Idempotency-Key` is replayed for the same API key with the same payload, StartupSeeker returns the existing job without charging the USD balance again.
- If the same idempotency key is reused with a different payload, the API returns `409 IDEMPOTENCY_MISMATCH`.
- If `webhook_endpoint_id` is supplied, StartupSeeker posts signed `api.job.succeeded` or `api.job.failed` events to that endpoint.

### List Webhook Endpoints

- **Method:** `GET`
- **Path:** `/api/v2/webhook-endpoints`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
GET /api/v2/webhook-endpoints
```

**Request fields**



**Response fields**

- `data.webhook_endpoints[].webhook_id` (string, optional) - Webhook endpoint identifier.
- `data.webhook_endpoints[].url` (string, optional) - Delivery URL.
- `data.webhook_endpoints[].label` (string | null, optional) - Optional human-readable label.
- `data.webhook_endpoints[].created_at / updated_at` (string, optional) - Lifecycle timestamps for the webhook endpoint.
- `meta` (object, optional) - Standard v2 metadata envelope.

**Sample request**

```bash
curl https://startup-seeker.com/api/v2/webhook-endpoints \
  -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{
  "data": {
    "webhook_endpoints": [
      {
        "webhook_id": "7c0d1e8b-302d-4d03-b38f-1ce85fa7999d",
        "url": "https://example.com/startupseeker/webhooks",
        "label": "Production jobs",
        "created_at": "2026-03-14T12:00:00.000Z",
        "updated_at": "2026-03-14T12:00:00.000Z"
      }
    ]
  },
  "meta": {
    "request_id": "b15df4f1-fbd3-4a5f-8249-a22294353106",
    "api_version": "v2",
    "duration_ms": 4,
    "billing": {"currency": "USD", "charged_usd": 0, "remaining_usd": 19.80 }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.
- `500 INTERNAL_ERROR` - Unexpected webhook endpoint listing failure.

**Notes**

- Webhook endpoints are account-managed resources shared across API keys on the same account.
- The signing secret is returned only on creation and is not listed again later.

### Create Webhook Endpoint

- **Method:** `POST`
- **Path:** `/api/v2/webhook-endpoints`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
{
  "url": "https://example.com/startupseeker/webhooks",
  "label": "Production jobs"
}
```

**Request fields**

- `url` (string, required) - HTTPS URL that receives job event deliveries.
- `label` (string | null, optional) - Optional human-readable label for the endpoint.

**Response fields**

- `data.webhook_id` (string, optional) - Webhook endpoint identifier.
- `data.url` (string, optional) - Delivery URL.
- `data.label` (string | null, optional) - Optional human-readable label.
- `data.signing_secret` (string, optional) - Secret used to verify `X-StartupSeeker-Signature`; returned only once.
- `meta` (object, optional) - Standard v2 metadata envelope.

**Sample request**

```bash
curl -X POST https://startup-seeker.com/api/v2/webhook-endpoints \
  -H "Authorization: Bearer ssk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/startupseeker/webhooks",
    "label": "Production jobs"
  }'
```

**Sample response**

```json
{
  "data": {
    "webhook_id": "7c0d1e8b-302d-4d03-b38f-1ce85fa7999d",
    "url": "https://example.com/startupseeker/webhooks",
    "label": "Production jobs",
    "signing_secret": "sskwhsec_11d6e95dcbcd19611e59191db79e2bb12309ad6b299f2717",
    "created_at": "2026-03-14T12:00:00.000Z",
    "updated_at": "2026-03-14T12:00:00.000Z"
  },
  "meta": {
    "request_id": "3c6d983e-74e7-4e1f-845c-362cebb41c35",
    "api_version": "v2",
    "duration_ms": 6,
    "billing": {"currency": "USD", "charged_usd": 0, "remaining_usd": 19.80 }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `400 BAD_REQUEST` - Invalid JSON or invalid webhook URL.
- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `409 CONFLICT` - A webhook endpoint with the same URL already exists on the account.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.
- `500 INTERNAL_ERROR` - Unexpected webhook endpoint creation failure.

**Notes**

- StartupSeeker signs deliveries using `X-StartupSeeker-Signature` and `X-StartupSeeker-Timestamp`.
- Webhook deliveries are retried with bounded exponential backoff and persisted in the delivery queue.

### Delete Webhook Endpoint

- **Method:** `DELETE`
- **Path:** `/api/v2/webhook-endpoints/{webhook_id}`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
DELETE /api/v2/webhook-endpoints/{webhook_id}
```

**Request fields**

- `webhook_id` (string, required) - Webhook endpoint identifier.

**Response fields**

- `data.webhook_id` (string, optional) - Deleted webhook endpoint identifier.
- `data.deleted` (boolean, optional) - Always `true` on success.
- `meta` (object, optional) - Standard v2 metadata envelope.

**Sample request**

```bash
curl -X DELETE https://startup-seeker.com/api/v2/webhook-endpoints/7c0d1e8b-302d-4d03-b38f-1ce85fa7999d \
  -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{
  "data": {
    "webhook_id": "7c0d1e8b-302d-4d03-b38f-1ce85fa7999d",
    "deleted": true
  },
  "meta": {
    "request_id": "f3dd0c13-9d18-48db-a06f-f5f8115f8599",
    "api_version": "v2",
    "duration_ms": 4,
    "billing": {"currency": "USD", "charged_usd": 0, "remaining_usd": 19.80 }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `404 NOT_FOUND` - Webhook endpoint does not exist or is not owned by the requesting account.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.
- `500 INTERNAL_ERROR` - Unexpected webhook endpoint deletion failure.

**Notes**

- Deleting a webhook endpoint stops future job deliveries to that URL.
- Queued deliveries for the deleted endpoint are removed via cascade delete.

### Deep Research Job Status

- **Method:** `GET`
- **Path:** `/api/v2/deep-research-jobs/{job_id}`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
GET /api/v2/deep-research-jobs/{job_id}
```

**Request fields**

- `job_id` (string, required) - Job identifier returned by the job creation endpoint.

**Response fields**

- `data.job_id` (string, optional) - Job identifier.
- `data.job_type` (string, optional) - Job type, currently `deep_research`.
- `data.status` (string, optional) - Job lifecycle state: `queued`, `running`, `succeeded`, or `failed`.
- `data.progress` (number, optional) - Coarse job progress percentage.
- `data.created_at / updated_at / completed_at` (string | null, optional) - Lifecycle timestamps for the job.
- `meta.job` (object, optional) - Job metadata mirrored into the standard v2 meta envelope.

**Sample request**

```bash
curl https://startup-seeker.com/api/v2/deep-research-jobs/d61d9b95-06f8-47a8-a409-77d9ee5756d1 \
  -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{
  "data": {
    "job_id": "d61d9b95-06f8-47a8-a409-77d9ee5756d1",
    "job_type": "deep_research",
    "status": "running",
    "progress": 10,
    "created_at": "2026-03-14T12:10:00.000Z",
    "updated_at": "2026-03-14T12:10:03.000Z",
    "completed_at": null
  },
  "meta": {
    "request_id": "a924f980-9d20-4b14-b168-d624e6dfb430",
    "api_version": "v2",
    "duration_ms": 6,
    "billing": {"currency": "USD", "charged_usd": 0, "remaining_usd": 19.80 },
    "job": {
      "id": "d61d9b95-06f8-47a8-a409-77d9ee5756d1",
      "status": "running"
    }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `404 NOT_FOUND` - Job does not exist or is not owned by the requesting API key.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.
- `500 INTERNAL_ERROR` - Unexpected job lookup failure.

**Notes**

- Status polling is rate-limited but not balance-charged.
- Ownership is enforced per API key; one key cannot inspect another key’s jobs.

### Deep Research Job Result

- **Method:** `GET`
- **Path:** `/api/v2/deep-research-jobs/{job_id}/result`
- **Auth mode:** `api_key_required`
- **Rate limit mode:** `api_key_and_account`
- **USD price:** $0.00 per request.
- **Content-Type:** `application/json`

**Request shape**

```
GET /api/v2/deep-research-jobs/{job_id}/result
```

**Request fields**

- `job_id` (string, required) - Job identifier returned by the job creation endpoint.

**Response fields**

- `data.job_id` (string, optional) - Job identifier.
- `data.status` (string, optional) - Final job state, typically `succeeded`.
- `data.result` (object, optional) - Deep research workflow result payload.
- `meta` (object, optional) - Standard v2 metadata envelope.

**Sample request**

```bash
curl https://startup-seeker.com/api/v2/deep-research-jobs/d61d9b95-06f8-47a8-a409-77d9ee5756d1/result \
  -H "Authorization: Bearer ssk_live_your_api_key"
```

**Sample response**

```json
{
  "data": {
    "job_id": "d61d9b95-06f8-47a8-a409-77d9ee5756d1",
    "status": "succeeded",
    "result": {
      "success": true,
      "zilliz_id": "458861125612149361",
      "timestamp": "2026-03-14T12:10:34.102Z",
      "data": {
        "deepScrape": {
          "status": "fulfilled",
          "website": "exa.ai"
        }
      }
    }
  },
  "meta": {
    "request_id": "0867f1dc-70b9-4d4a-9b01-0efc8f4d6720",
    "api_version": "v2",
    "duration_ms": 6,
    "billing": {"currency": "USD", "charged_usd": 0, "remaining_usd": 19.80 },
    "job": {
      "id": "d61d9b95-06f8-47a8-a409-77d9ee5756d1",
      "status": "succeeded"
    }
  }
}
```

**Important headers**

- `Authorization` - Required. `Bearer ssk_live_...` or `ssk_test_...` API key.
- `X-Balance-Remaining-USD` - USD balance remaining after the request finalizes.

**Common errors**

- `401 UNAUTHORIZED` - Missing or invalid API key.
- `403 FORBIDDEN` - API key is inactive, retired, expired, or not valid for this environment.
- `404 NOT_FOUND` - Job does not exist or is not owned by the requesting API key.
- `409 JOB_NOT_READY` - Job exists but has not completed yet.
- `422 JOB_FAILED` - Job reached a failed terminal state; inspect `error.details`.
- `429 RATE_LIMITED` - Per-key or per-account quota exceeded.

**Notes**

- The final result endpoint is rate-limited but not balance-charged.
- The embedded `data.result` payload reflects the current deep-research workflow output from the shared processor.

## Error Handling

- Error payloads are not fully uniform across endpoints. The examples above show the current shape for each route.
- Paid endpoints may also return `402 INSUFFICIENT_FUNDS`.
