StartupSeeker Public API v2
This reference documents the v2 public API. The page, the Markdown export, the OpenAPI JSON, and the README overview are generated from the same v2 endpoint catalog.
Official Surface
The reference covers the `/api/v2/*` public endpoints, including async job creation, polling, and result retrieval.
Current Behavior
The docs reflect the current v2 contract: API-key-only public access, unified envelopes, and async jobs for long-running workflows.
Generated Markdown
Use the export below when you need to feed the selected API version to an LLM, internal tool, or external consumer.
Discovery and Analysis
Core synchronous endpoints for search, lookups, founder discovery, competitor analysis, and startup screening.
/api/v2/searchesCanonical search endpoint for natural-language startup discovery.
/api/v2/company-lookupsBatch website lookup endpoint for fetching up to 100 StartupSeeker company records in one call.
/api/v2/foundersFounder discovery endpoint that accepts only a website and returns the standard v2 response envelope.
/api/v2/competitor-analysesCompetitor analysis endpoint with the standard v2 response envelope.
/api/v2/vc-backability-checksVC-backability endpoint replacing both legacy classifier routes.
Pitch Deck Jobs
Create, recover, poll, and fetch results for asynchronous pitch-deck parsing.
/api/v2/pitch-deck-jobsCreate an asynchronous pitch-deck parsing job.
/api/v2/pitch-deck-jobsList recent pitch-deck jobs owned by the authenticated API key so clients can recover saved job IDs.
/api/v2/pitch-deck-jobs/{job_id}Poll the status of an asynchronous pitch-deck job.
/api/v2/pitch-deck-jobs/{job_id}/resultFetch the completed result for an asynchronous pitch-deck job.
Deep Research Jobs
Create, recover, poll, and fetch results for asynchronous deep-research runs.
/api/v2/deep-research-jobsCreate an asynchronous deep-research job. Use this route instead of the legacy `/api/deep-research` endpoint for new integrations.
/api/v2/deep-research-jobsList recent deep-research jobs owned by the authenticated API key so clients can recover saved job IDs.
/api/v2/deep-research-jobs/{job_id}Poll the status of an asynchronous deep-research job.
/api/v2/deep-research-jobs/{job_id}/resultFetch the completed result for an asynchronous deep-research job.
Webhooks
Manage account-level webhook endpoints for async job completion events.
/api/v2/webhook-endpointsCreate a managed webhook endpoint for async job completion events.
/api/v2/webhook-endpointsList managed webhook endpoints for the authenticated account.
/api/v2/webhook-endpoints/{webhook_id}Delete a managed webhook endpoint owned by the authenticated account.
Copy Docs for LLMs
Copy the same V2 public API reference that powers this page, formatted as Markdown.
OpenAPI JSON
Download the generated machine-readable v2 spec for schema tooling, SDK generation, or external validation.
Authentication and Access Modes
Public v2 access uses a single auth model: send a StartupSeeker API key in the `Authorization` header. Signed-in browser flows use internal routes and are intentionally not part of the public v2 contract.
Bearer token format
Use the same bearer token header across the full v2 API surface.
Authorization: Bearer ssk_live_your_api_keyAsync workflow model
Deep research and pitch-deck parsing use async jobs in v2. Create a job, poll its status, then fetch the final result.
/api/v2/searchesSearches
Use this route when you want StartupSeeker to turn a natural-language sourcing prompt into a ranked list of companies. It is the main discovery endpoint for prospecting, lead generation, market mapping, and investor research workflows.
You can combine a free-text query with structured filters, and the response comes back in the standard v2 envelope with interpreted search metadata. That makes it a good fit for both UI search experiences and backend pipelines that need consistent pagination, filtering, and auditability.
Search Result Cap
The `limit` field on `/api/v2/searches` is capped at `50`. Requests above `50` are rejected with `400 BAD_REQUEST`.
Request Shape
{
"request_id": "search-v2-demo-1",
"query": "AI startups helping ecommerce teams with demand forecasting",
"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
| Name | Type | Required | Description |
|---|---|---|---|
| request_id | string | null | No | Optional client correlation ID. Maximum 128 characters. |
| query | string | Yes | Search query. Must be between 3 and 500 characters. |
| limit | number | null | No | Optional result cap between 1 and 50. Defaults to 25. |
| filters | object | null | No | Optional structured filter object passed into the main search pipeline. Unknown keys are rejected. |
| filters.foundedYear | { min?: number | null; max?: number | null; includeMissing?: boolean } | No | Filter by founded year. Use `includeMissing: true` to also include companies with unknown founded year. |
| filters.funding | { min?: number | null; max?: number | null } | No | Filter by total funding in USD. |
| filters.employees | { min?: number | null; max?: number | null } | No | Filter by LinkedIn employee count. |
| filters.followers | { min?: number | null; max?: number | null } | No | Filter by LinkedIn follower count. |
| filters.score | { min?: number | null; max?: number | null } | No | Filter by StartupSeeker score on the 0-10 scale. |
| filters.countries | { include?: string[]; exclude?: string[] } | No | Include or exclude ISO-2 country codes such as `US`, `DE`, or `FR`. |
| filters.cities | string[] | No | Restrict results to one or more city names. |
| filters.regions | string[] | No | Restrict results to one or more region, state, or province names. |
| filters.industries | { values: string[]; operator?: "AND" | "OR" } | No | Match one or more normalized industry/category labels. `OR` is the default when omitted. |
| filters.lists | { include?: uuid[]; exclude?: uuid[] } | No | Include or exclude companies that belong to your saved lists by StartupSeeker list ID. |
| filters.websites | { include?: string[]; exclude?: string[] } | No | Include or exclude specific company domains. Domains are normalized before matching. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.query | string | No | Echoes the final query used for the search. |
| data.total_results | number | No | Number of returned results. |
| data.results | array<object> | No | Structured company results. |
| data.active_filters | object | No | Resolved filters after query interpretation. This can include any supported filter key plus `semanticQuery`. |
| data.active_filters.semanticQuery | string | No | Semantic query text actually used after the refinement stage. |
| data.request | object | No | Request metadata emitted by the search service. |
| meta | object | No | Standard v2 metadata envelope with request ID, duration, rate-limit state, and credits. |
Sample Request
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",
"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
{
"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
}
},
"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 }
},
"credits": {
"charged": 1,
"remaining": 99
}
}
}Important Headers
| Header | Description |
|---|---|
| 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-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Invalid JSON, invalid `query`, or invalid filter structure. |
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 402 | INSUFFICIENT_CREDITS | API key is valid but the account has no remaining credits. |
| 429 | RATE_LIMITED | Per-key or per-account quota exceeded. |
| 500 | INTERNAL_ERROR | Unexpected search failure. |
Implementation Notes
- Use this route instead of `/api/v1/search` and `/api/assistant/search` for new integrations.
- The request `limit` is capped at `50`; values above `50` are rejected with `400 BAD_REQUEST`.
- 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.
/api/v2/company-lookupsCompany Lookups
Use this route to check a batch of company websites against the StartupSeeker dataset in a single call. It is the fastest way to power CRM syncs, imports, enrichment queues, and deduplication workflows when you already know the domains you care about.
The API normalizes each submitted website, preserves input order in the response, and returns a per-item match or miss record in `data.companies`. That makes it straightforward to merge the results back into your own systems without losing positional context.
Request Shape
{
"request_id": "lookup-batch-demo-1",
"websites": ["stripe.com", "exa.ai"]
}Request Fields
| Name | Type | Required | Description |
|---|---|---|---|
| request_id | string | null | No | Optional client correlation ID. Maximum 128 characters. |
| websites | array<string> | Yes | Batch-only input. Supply 1 to 100 company websites or domains. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.companies | array<object> | No | Ordered lookup results in the same order as the submitted `websites` array. |
| data.companies[].submitted | string | No | Original submitted website string. |
| data.companies[].normalized | string | No | Normalized domain used for matching. |
| data.companies[].company | object | null | No | Matching company record, or `null` when no match exists. |
| data.companies[].missing | boolean | No | Convenience flag indicating whether the company record was missing. |
| meta | object | No | Standard v2 metadata envelope with rate-limit and credit state. |
Sample Request
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",
"websites": ["stripe.com", "exa.ai"]
}'Sample Response
{
"data": {
"companies": [
{
"submitted": "stripe.com",
"normalized": "stripe.com",
"company": {
"id": "458861125612149361",
"name": "Stripe",
"website": "stripe.com"
},
"missing": false
},
{
"submitted": "exa.ai",
"normalized": "exa.ai",
"company": null,
"missing": true
}
]
},
"meta": {
"request_id": "lookup-batch-demo-1",
"api_version": "v2",
"duration_ms": 180,
"credits": { "charged": 0, "remaining": 99 }
}
}Important Headers
| Header | Description |
|---|---|
| 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-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Invalid JSON, missing `websites`, empty arrays, arrays longer than 100, or invalid website input. |
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 429 | RATE_LIMITED | Per-key or per-account quota exceeded. |
| 500 | INTERNAL_ERROR | Website lookup service is not configured or failed. |
Implementation Notes
- Use this route instead of `/api/v1/enrich` for new integrations.
- This v2 route is batch-only; use `/api/v1/enrich` if you need the legacy single-company response shape.
- Results preserve input order and duplicates from the submitted `websites` array.
- A 200 response with `data.companies[].missing: true` is the expected result when StartupSeeker has no matching company.
/api/v2/foundersFounders
Use this route when you want StartupSeeker to identify founders and the current CEO for a company using only its website. It is designed for workflows that want a simpler public contract than the legacy founder route while still benefiting from the existing founder discovery pipeline.
StartupSeeker opportunistically reuses company lookup data to improve the founder run, but a lookup miss does not block execution. The response includes normalized website metadata, optional resolved company context, founding details, and the discovered founder profiles in the standard v2 envelope.
Request Shape
{
"request_id": "founders-v2-demo-1",
"website": "stripe.com"
}Request Fields
| Name | Type | Required | Description |
|---|---|---|---|
| request_id | string | null | No | Optional client correlation ID. Maximum 128 characters. |
| website | string | Yes | Website or domain to analyze for founder discovery. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.website.submitted | string | No | Original submitted website string. |
| data.website.normalized | string | No | Normalized website used for lookup and pipeline execution. |
| data.company | object | null | No | Best-effort company context resolved from existing StartupSeeker lookup data, or `null` when no match exists. |
| data.company.name | string | null | No | Resolved company name when lookup enrichment was available. |
| data.company.description | string | null | No | Resolved company description when available from lookup enrichment. |
| data.company.resolved_from_lookup | boolean | No | Always `true` when `data.company` is present. |
| data.founding_details | object | No | Structured founding details returned by the founder pipeline. |
| data.founders | array<object> | No | Founder and CEO records discovered by the pipeline. |
| meta | object | No | Standard v2 metadata envelope with rate-limit and credit state. |
Sample Request
curl -X POST https://startup-seeker.com/api/v2/founders \
-H "Authorization: Bearer ssk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"request_id": "founders-v2-demo-1",
"website": "stripe.com"
}'Sample Response
{
"data": {
"website": {
"submitted": "stripe.com",
"normalized": "stripe.com"
},
"company": {
"name": "Stripe",
"description": "Payments infrastructure for internet businesses.",
"resolved_from_lookup": true
},
"founding_details": {
"year_founded": "2010",
"location": "San Francisco, United States"
},
"founders": [
{
"name": "Patrick Collison",
"role": "Co-founder & CEO",
"status": "active",
"linkedin_profile_url": "https://linkedin.com/in/patrickcollison",
"location": {
"city": "San Francisco",
"country": "United States"
},
"experience_summary": "- **Stripe**, Co-founder & CEO",
"education": [],
"sources": ["https://stripe.com/about"]
}
]
},
"meta": {
"request_id": "founders-v2-demo-1",
"api_version": "v2",
"duration_ms": 1520,
"credits": { "charged": 5, "remaining": 95 }
}
}Important Headers
| Header | Description |
|---|---|
| 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-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Invalid JSON or invalid `website` input. |
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 402 | INSUFFICIENT_CREDITS | Account has no remaining API credits. |
| 429 | RATE_LIMITED | Per-key or per-account quota exceeded. |
| 500 | INTERNAL_ERROR | Founder discovery failed or insufficient context was available. |
Implementation Notes
- This route accepts only a website in the public v2 contract.
- StartupSeeker reuses existing company data when available, but still attempts founder discovery when no match exists.
- Successful responses always use the v2 `data` + `meta` envelope.
/api/v2/competitor-analysesCompetitor Analyses
Use this route when you already know the target company and want StartupSeeker to generate a focused competitor set. Providing a website gives the resolver the strongest signal, while a description can be used as a fallback when no domain is available.
The response includes the resolved company context, the generated search query, and the returned competitors with similarity information. It is designed for product experiences that need explainable comps rather than a generic company search result list.
Request Shape
{
"request_id": "competitor-v2-demo-1",
"company": {
"name": "Acme Analytics",
"website": "acmeanalytics.com"
}
}Request Fields
| Name | Type | Required | Description |
|---|---|---|---|
| request_id | string | null | No | Optional client correlation ID. Maximum 128 characters. |
| company.name | string | null | No | Optional company name used to improve resolution. |
| company.website | string | null | No | Optional website. Provide this whenever possible. |
| company.description | string | null | No | Optional description. Required when no website is provided. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.company | object | No | Contains the input payload and resolved company context. |
| data.competitors | array<object> | No | Competitor matches and AI-generated summaries. |
| data.total_analyzed | number | No | Number of competitor records returned. |
| data.search_query | string | null | No | Search query generated by the competitor analysis service. |
| meta | object | No | Standard v2 metadata envelope with rate-limit and credit state. |
Sample Request
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
{
"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"
}
],
"total_analyzed": 1,
"search_query": "analytics workflow competitors"
},
"meta": {
"request_id": "competitor-v2-demo-1",
"api_version": "v2",
"duration_ms": 920,
"credits": { "charged": 0, "remaining": 99 }
}
}Important Headers
| Header | Description |
|---|---|
| Authorization | Required. `Bearer ssk_live_...` or `ssk_test_...` API key. |
| X-RateLimit-Limit-Minute | Effective per-minute limit for the key/account. |
| X-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Invalid JSON or missing both `company.website` and `company.description`. |
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 429 | RATE_LIMITED | Per-key or per-account quota exceeded. |
| 500 | INTERNAL_ERROR | Competitor analysis failed. |
Implementation Notes
- Use this route instead of `/api/v1/competitors` for new integrations.
- Provide `company.website` whenever possible for the most stable resolution path.
/api/v2/vc-backability-checksVC Backability Checks
Use this route to quickly screen one or more startups for venture-backability. It is intended for fast triage when you want a classification before deciding whether a company deserves deeper manual review or a full research run.
Batch input is supported through `websites`, and `response_detail` lets you choose between a compact verdict-only payload and a fuller response with more supporting output. That makes the same route usable for both high-volume scoring and analyst-facing tooling.
Request Shape
{
"request_id": "vc-v2-demo-1",
"websites": ["runwayml.com", "genei.io"],
"response_detail": "minimal"
}Request Fields
| Name | Type | Required | Description |
|---|---|---|---|
| request_id | string | null | No | Optional client correlation ID. Maximum 128 characters. |
| websites | array<string> | null | No | Preferred batch input. Supply one or more websites/domains. |
| website | string | null | No | Single-site shortcut when not using `websites`. |
| response_detail | "minimal" | "full" | null | No | Controls whether the response contains full scrape/detail output or a compact verdict set. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.count | number | No | Number of entries returned in `data.results`. |
| data.results | array<object> | object | No | Full or minimal classification payload depending on `response_detail`. |
| data.results[website].classification | "Yes" | "Likely Not" | No | Minimal-mode VC backability verdict. |
| data.results[website].name | string | null | No | Minimal-mode company name when available. |
| data.results[website].description | string | null | No | Minimal-mode short description (cached) or generated AI description (fresh scrape). |
| meta | object | No | Standard v2 metadata envelope with rate-limit and credit state. |
Sample Request
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
{
"data": {
"count": 2,
"results": {
"runwayml.com": {
"classification": "Yes",
"name": "Runway",
"description": "AI-powered creative tools for generating and editing video and image content."
},
"genei.io": {
"classification": "Likely Not",
"name": "Genei",
"description": "Research and note-taking assistant focused on summarization and productivity workflows."
}
}
},
"meta": {
"request_id": "vc-v2-demo-1",
"api_version": "v2",
"duration_ms": 2140,
"credits": { "charged": 0, "remaining": 99 }
}
}Important Headers
| Header | Description |
|---|---|
| Authorization | Required. `Bearer ssk_live_...` or `ssk_test_...` API key. |
| X-RateLimit-Limit-Minute | Effective per-minute limit for the key/account. |
| X-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Request omitted both `website` and `websites`, or validation failed. |
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 429 | RATE_LIMITED | Per-key or per-account quota exceeded. |
| 500 | INTERNAL_ERROR | Required scraping configuration is missing or classification failed. |
Implementation Notes
- Use this route instead of `/api/v1/vc-backable-classifier` and `/api/v1/vc-backable-minimum` for new integrations.
- Choose `response_detail: "minimal"` for a compact verdict payload that still includes `name` and `description`.
/api/v2/pitch-deck-jobsList Pitch Deck Jobs
Use this listing endpoint to recover, inspect, or paginate through pitch-deck jobs created by the current API key. It is mainly operational and helps when a client lost local job state, a webhook was missed, or you need to rebuild a queue view.
The response is intentionally lightweight: each item includes status, timestamps, and relative poll/result URLs. You can resume tracking existing jobs without re-uploading the original deck.
Request Shape
GET /api/v2/pitch-deck-jobs?status=running&limit=10&request_id=pitch-deck-demo-1Request Fields
| Name | Type | Required | Description |
|---|---|---|---|
| status | queued | running | succeeded | failed | No | Optional status filter. |
| limit | integer | No | Optional page size from 1 to 100. Defaults to `20`. |
| after | string | No | Optional cursor returned as `data.next_cursor` for pagination. |
| request_id | string | No | Optional exact-match filter on the original request ID. |
| idempotency_key | string | No | Optional exact-match filter on the original idempotency key. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.jobs[].job_id | string | No | Job identifier. |
| data.jobs[].request_id | string | null | No | Original client request ID, when supplied. |
| data.jobs[].status | string | No | Current job lifecycle state. |
| data.jobs[].created_at / updated_at / completed_at | string | null | No | Lifecycle timestamps for the job. |
| data.jobs[].poll_url | string | No | Relative path for polling job status. Prefix it with your API base URL. |
| data.jobs[].result_url | string | No | Relative path for retrieving the completed result. Prefix it with your API base URL. |
| data.next_cursor | string | null | No | Opaque cursor for the next page, or `null` when exhausted. |
| meta | object | No | Standard v2 metadata envelope. |
Sample Request
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
{
"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,
"credits": { "charged": 0, "remaining": 95 }
}
}Important Headers
| Header | Description |
|---|---|
| Authorization | Required. `Bearer ssk_live_...` or `ssk_test_...` API key. |
| X-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Query parameter validation failed. |
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 429 | RATE_LIMITED | Per-key or per-account quota exceeded. |
| 500 | INTERNAL_ERROR | Unexpected job listing failure. |
Implementation 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.
/api/v2/pitch-deck-jobsCreate Pitch Deck Job
Use this route to upload a PDF pitch deck for asynchronous parsing. StartupSeeker accepts the file, creates a job immediately, and processes the deck in the background instead of holding the request open until extraction is finished.
This is the right entry point when you need structured deck output in production systems. Create the job once, then either poll the paired status/result endpoints or attach a managed webhook endpoint to receive completion events.
Request Shape
multipart/form-data
- file: <PDF, required>
- request_id: <string, optional>
- webhook_endpoint_id: <uuid, optional>Request Fields
| Name | Type | Required | Description |
|---|---|---|---|
| file | File | Yes | Pitch deck PDF. Maximum 30 MB. |
| request_id | string | null | No | Optional client correlation ID. |
| webhook_endpoint_id | uuid | null | No | Optional managed webhook endpoint that receives job completion events. |
| Idempotency-Key | header | No | 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
| Name | Type | Required | Description |
|---|---|---|---|
| data.job_id | string | No | Created or reused job identifier. |
| data.status | string | No | Initial job status, typically `queued`. |
| data.poll_url | string | No | Relative path for polling job status. Prefix it with your API base URL. |
| data.result_url | string | No | Relative path for retrieving the completed result. Prefix it with your API base URL. |
| meta.job | object | No | Job metadata mirrored into the standard v2 meta envelope. |
Sample Request
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
{
"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,
"credits": { "charged": 5, "remaining": 95 },
"job": {
"id": "3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4",
"status": "queued"
}
}
}Important Headers
| Header | Description |
|---|---|
| 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-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Request was not multipart/form-data or no PDF was supplied. |
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 402 | INSUFFICIENT_CREDITS | Account has no remaining API credits. |
| 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. |
Implementation Notes
- Use this route instead of `/api/v1/parse-deck` for new 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 credits 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.
/api/v2/pitch-deck-jobs/{job_id}Pitch Deck Job Status
Use this route to monitor a pitch-deck job after creation. It reports lifecycle state, coarse progress, and timing information, but it does not return the parsed deck payload itself.
Poll this endpoint while the job is `queued` or `running`, then switch to the result endpoint once the job reaches `succeeded`. That keeps status checks cheap and keeps the final payload fetch separate from progress monitoring.
Request Shape
GET /api/v2/pitch-deck-jobs/{job_id}Request Fields
| Name | Type | Required | Description |
|---|---|---|---|
| job_id | string | Yes | Job identifier returned by the job creation endpoint. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.job_id | string | No | Job identifier. |
| data.job_type | string | No | Job type, currently `pitch_deck`. |
| data.status | string | No | Job lifecycle state: `queued`, `running`, `succeeded`, or `failed`. |
| data.progress | number | No | Coarse job progress percentage. |
| data.created_at / updated_at / completed_at | string | null | No | Lifecycle timestamps for the job. |
| meta.job | object | No | Job metadata mirrored into the standard v2 meta envelope. |
Sample Request
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
{
"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,
"credits": { "charged": 0, "remaining": 95 },
"job": {
"id": "3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4",
"status": "running"
}
}
}Important Headers
| Header | Description |
|---|---|
| Authorization | Required. `Bearer ssk_live_...` or `ssk_test_...` API key. |
| X-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 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. |
Implementation Notes
- Status polling is rate-limited but not credit-charged.
- Ownership is enforced per API key; one key cannot inspect another key’s jobs.
/api/v2/pitch-deck-jobs/{job_id}/resultPitch Deck Job Result
Use this route only after a pitch-deck job has completed successfully. It returns the final parsed output for the uploaded deck in the standard v2 response envelope.
Clients should treat this as the terminal fetch step in the workflow: `409 JOB_NOT_READY` means the job is still processing, while `422 JOB_FAILED` means the job finished unsuccessfully and should be handled as a terminal error rather than retried blindly.
Request Shape
GET /api/v2/pitch-deck-jobs/{job_id}/resultRequest Fields
| Name | Type | Required | Description |
|---|---|---|---|
| job_id | string | Yes | Job identifier returned by the job creation endpoint. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.job_id | string | No | Job identifier. |
| data.status | string | No | Final job state, typically `succeeded`. |
| data.result | object | No | Pitch-deck parsing result with slide summaries and markdown. |
| meta | object | No | Standard v2 metadata envelope. |
Sample Request
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
{
"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,
"credits": { "charged": 0, "remaining": 95 },
"job": {
"id": "3c6a6176-1f8d-4e8d-b1a0-9bf9d4b6e7d4",
"status": "succeeded"
}
}
}Important Headers
| Header | Description |
|---|---|
| Authorization | Required. `Bearer ssk_live_...` or `ssk_test_...` API key. |
| X-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 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. |
Implementation Notes
- The final result endpoint is rate-limited but not credit-charged.
- Failed jobs return the standard v2 error envelope instead of a success body.
/api/v2/deep-research-jobsList Deep Research Jobs
Use this listing endpoint to recover, inspect, or paginate through deep-research jobs created by the current API key. It is useful for dashboards, webhook reconciliation, support tooling, and any workflow that needs to resume work after losing a job ID.
Each item includes status, timestamps, and relative poll/result URLs rather than the full research payload. That keeps the list endpoint fast while still giving clients everything they need to continue tracking or fetching completed runs.
Request Shape
GET /api/v2/deep-research-jobs?status=succeeded&limit=10&request_id=deep-research-v2-demo-1Request Fields
| Name | Type | Required | Description |
|---|---|---|---|
| status | queued | running | succeeded | failed | No | Optional status filter. |
| limit | integer | No | Optional page size from 1 to 100. Defaults to `20`. |
| after | string | No | Optional cursor returned as `data.next_cursor` for pagination. |
| request_id | string | No | Optional exact-match filter on the original request ID. |
| idempotency_key | string | No | Optional exact-match filter on the original idempotency key. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.jobs[].job_id | string | No | Job identifier. |
| data.jobs[].request_id | string | null | No | Original client request ID, when supplied. |
| data.jobs[].status | string | No | Current job lifecycle state. |
| data.jobs[].created_at / updated_at / completed_at | string | null | No | Lifecycle timestamps for the job. |
| data.jobs[].poll_url | string | No | Relative path for polling job status. Prefix it with your API base URL. |
| data.jobs[].result_url | string | No | Relative path for retrieving the completed result. Prefix it with your API base URL. |
| data.next_cursor | string | null | No | Opaque cursor for the next page, or `null` when exhausted. |
| meta | object | No | Standard v2 metadata envelope. |
Sample Request
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
{
"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,
"credits": { "charged": 0, "remaining": 90 }
}
}Important Headers
| Header | Description |
|---|---|
| Authorization | Required. `Bearer ssk_live_...` or `ssk_test_...` API key. |
| X-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Query parameter validation failed. |
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 429 | RATE_LIMITED | Per-key or per-account quota exceeded. |
| 500 | INTERNAL_ERROR | Unexpected job listing failure. |
Implementation 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.
/api/v2/deep-research-jobsCreate Deep Research Job
Use this route to start StartupSeeker's full deep-research workflow for a company website. The job runs asynchronously because it can combine company resolution, web research, and optional competitor analysis into a heavier report.
You can steer the run with company, location, and analyst context fields, disable competitor analysis when you only need the core company report, and attach a managed webhook for completion events. It is the recommended v2 replacement for the older synchronous deep-research flow.
Request Shape
{
"request_id": "deep-research-v2-demo-1",
"website": "exa.ai",
"include_competitors": false,
"company_name": "Exa"
}Request Fields
| Name | Type | Required | Description |
|---|---|---|---|
| request_id | string | null | No | Optional client correlation ID. Maximum 128 characters. |
| website | string | Yes | Website/domain to research. |
| zilliz_id | string | null | No | Optional existing StartupSeeker record ID. |
| include_competitors | boolean | null | No | Optional. Defaults to `true`. Set to `false` to skip competitor analysis. |
| city / country / country_iso | string | null | No | Optional location overrides used during the workflow. Use ISO-2 codes such as `US` or `DE` for `country_iso`. |
| context | string | null | No | Optional analyst context merged into the deep research workflow. |
| company_name | string | null | No | Optional company name override. |
| webhook_endpoint_id | uuid | null | No | Optional managed webhook endpoint that receives job completion events. |
| Idempotency-Key | header | No | 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
| Name | Type | Required | Description |
|---|---|---|---|
| data.job_id | string | No | Created or reused job identifier. |
| data.status | string | No | Initial job status, typically `queued`. |
| data.poll_url | string | No | Relative path for polling job status. Prefix it with your API base URL. |
| data.result_url | string | No | Relative path for retrieving the completed result. Prefix it with your API base URL. |
| meta.job | object | No | Job metadata mirrored into the standard v2 meta envelope. |
Sample Request
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",
"include_competitors": false,
"company_name": "Exa",
"webhook_endpoint_id": "7c0d1e8b-302d-4d03-b38f-1ce85fa7999d"
}'Sample Response
{
"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,
"credits": { "charged": 10, "remaining": 90 },
"job": {
"id": "d61d9b95-06f8-47a8-a409-77d9ee5756d1",
"status": "queued"
}
}
}Important Headers
| Header | Description |
|---|---|
| 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-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Invalid JSON, missing `website`, or schema validation failed. |
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 402 | INSUFFICIENT_CREDITS | Account has no remaining API credits. |
| 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. |
Implementation Notes
- Public API access is API-key-only in v2.
- The legacy synchronous `/api/deep-research` route remains available as a compatibility layer during migration.
- If the same `Idempotency-Key` is replayed for the same API key with the same payload, StartupSeeker returns the existing job without charging credits 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.
/api/v2/webhook-endpointsList Webhook Endpoints
Use this route to inspect the managed webhook destinations configured for the authenticated account. It is primarily a settings and operations endpoint for building admin screens, selecting a webhook to attach to new jobs, or auditing which URLs are active.
Webhook endpoints are account-scoped resources shared across API keys on the same account. Listing them here gives clients a stable source of truth before creating or deleting endpoints elsewhere in the API.
Request Shape
GET /api/v2/webhook-endpointsRequest Fields
| Name | Type | Required | Description |
|---|
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.webhook_endpoints[].webhook_id | string | No | Webhook endpoint identifier. |
| data.webhook_endpoints[].url | string | No | Delivery URL. |
| data.webhook_endpoints[].label | string | null | No | Optional human-readable label. |
| data.webhook_endpoints[].created_at / updated_at | string | No | Lifecycle timestamps for the webhook endpoint. |
| meta | object | No | Standard v2 metadata envelope. |
Sample Request
curl https://startup-seeker.com/api/v2/webhook-endpoints \
-H "Authorization: Bearer ssk_live_your_api_key"Sample Response
{
"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,
"credits": { "charged": 0, "remaining": 90 }
}
}Important Headers
| Header | Description |
|---|---|
| Authorization | Required. `Bearer ssk_live_...` or `ssk_test_...` API key. |
| X-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 429 | RATE_LIMITED | Per-key or per-account quota exceeded. |
| 500 | INTERNAL_ERROR | Unexpected webhook endpoint listing failure. |
Implementation 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.
/api/v2/webhook-endpointsCreate Webhook Endpoint
Use this route to register an HTTPS endpoint that should receive signed async job events from StartupSeeker. Once created, the returned `webhook_endpoint_id` can be attached to pitch-deck and deep-research job creation requests.
The signing secret is returned only once when the endpoint is created. Persist it immediately and use it to verify `X-StartupSeeker-Signature` on incoming deliveries before accepting webhook payloads.
Request Shape
{
"url": "https://example.com/startupseeker/webhooks",
"label": "Production jobs"
}Request Fields
| Name | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | HTTPS URL that receives job event deliveries. |
| label | string | null | No | Optional human-readable label for the endpoint. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.webhook_id | string | No | Webhook endpoint identifier. |
| data.url | string | No | Delivery URL. |
| data.label | string | null | No | Optional human-readable label. |
| data.signing_secret | string | No | Secret used to verify `X-StartupSeeker-Signature`; returned only once. |
| meta | object | No | Standard v2 metadata envelope. |
Sample Request
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
{
"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,
"credits": { "charged": 0, "remaining": 90 }
}
}Important Headers
| Header | Description |
|---|---|
| Authorization | Required. `Bearer ssk_live_...` or `ssk_test_...` API key. |
| X-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Invalid JSON or invalid webhook URL. |
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 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. |
Implementation 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.
/api/v2/webhook-endpoints/{webhook_id}Delete Webhook Endpoint
Use this route to remove a managed webhook destination that should no longer receive StartupSeeker events. This is the cleanup path for rotated URLs, decommissioned environments, or endpoints that were created by mistake.
Deleting the endpoint stops future deliveries to that URL and removes queued deliveries tied to it. Use the `webhook_id` from the list or create response to target the correct destination.
Request Shape
DELETE /api/v2/webhook-endpoints/{webhook_id}Request Fields
| Name | Type | Required | Description |
|---|---|---|---|
| webhook_id | string | Yes | Webhook endpoint identifier. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.webhook_id | string | No | Deleted webhook endpoint identifier. |
| data.deleted | boolean | No | Always `true` on success. |
| meta | object | No | Standard v2 metadata envelope. |
Sample Request
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
{
"data": {
"webhook_id": "7c0d1e8b-302d-4d03-b38f-1ce85fa7999d",
"deleted": true
},
"meta": {
"request_id": "f3dd0c13-9d18-48db-a06f-f5f8115f8599",
"api_version": "v2",
"duration_ms": 4,
"credits": { "charged": 0, "remaining": 90 }
}
}Important Headers
| Header | Description |
|---|---|
| Authorization | Required. `Bearer ssk_live_...` or `ssk_test_...` API key. |
| X-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 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. |
Implementation Notes
- Deleting a webhook endpoint stops future job deliveries to that URL.
- Queued deliveries for the deleted endpoint are removed via cascade delete.
/api/v2/deep-research-jobs/{job_id}Deep Research Job Status
Use this route to monitor a deep-research job after creation. It returns lifecycle state, coarse progress, and timestamps so clients can show job progress without downloading the full report body on every poll.
Poll this endpoint while the job is `queued` or `running`, then fetch the final research payload from the result endpoint after the job reaches `succeeded`. Ownership is enforced per API key, so a key can only inspect its own jobs.
Request Shape
GET /api/v2/deep-research-jobs/{job_id}Request Fields
| Name | Type | Required | Description |
|---|---|---|---|
| job_id | string | Yes | Job identifier returned by the job creation endpoint. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.job_id | string | No | Job identifier. |
| data.job_type | string | No | Job type, currently `deep_research`. |
| data.status | string | No | Job lifecycle state: `queued`, `running`, `succeeded`, or `failed`. |
| data.progress | number | No | Coarse job progress percentage. |
| data.created_at / updated_at / completed_at | string | null | No | Lifecycle timestamps for the job. |
| meta.job | object | No | Job metadata mirrored into the standard v2 meta envelope. |
Sample Request
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
{
"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,
"credits": { "charged": 0, "remaining": 90 },
"job": {
"id": "d61d9b95-06f8-47a8-a409-77d9ee5756d1",
"status": "running"
}
}
}Important Headers
| Header | Description |
|---|---|
| Authorization | Required. `Bearer ssk_live_...` or `ssk_test_...` API key. |
| X-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 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. |
Implementation Notes
- Status polling is rate-limited but not credit-charged.
- Ownership is enforced per API key; one key cannot inspect another key’s jobs.
/api/v2/deep-research-jobs/{job_id}/resultDeep Research Job Result
Use this route to fetch the final output of a completed deep-research job. It returns the assembled research payload once StartupSeeker has finished the workflow.
Treat it as the final step in the async flow: `409 JOB_NOT_READY` means the report is still being generated, while `422 JOB_FAILED` indicates a terminal failure and points clients toward error handling rather than continued polling.
Request Shape
GET /api/v2/deep-research-jobs/{job_id}/resultRequest Fields
| Name | Type | Required | Description |
|---|---|---|---|
| job_id | string | Yes | Job identifier returned by the job creation endpoint. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.job_id | string | No | Job identifier. |
| data.status | string | No | Final job state, typically `succeeded`. |
| data.result | object | No | Deep research workflow result payload. |
| meta | object | No | Standard v2 metadata envelope. |
Sample Request
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
{
"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,
"credits": { "charged": 0, "remaining": 90 },
"job": {
"id": "d61d9b95-06f8-47a8-a409-77d9ee5756d1",
"status": "succeeded"
}
}
}Important Headers
| Header | Description |
|---|---|
| Authorization | Required. `Bearer ssk_live_...` or `ssk_test_...` API key. |
| X-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 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. |
Implementation Notes
- The final result endpoint is rate-limited but not credit-charged.
- The embedded `data.result` payload reflects the current deep-research workflow output from the shared processor.
Rate Limits
All public v2 routes use the metered API-key quota model. Long-running workflows differ by transport, not by rate-limit family.
Header Behavior
- Metered API-key routes return `X-RateLimit-*` headers and `X-Credits-Remaining`.
- Job creation endpoints charge credits; job polling and job result endpoints are rate-limited but use `0` credits.
- All successful public v2 responses use the `data` + `meta` envelope.
Errors
v2 standardizes public errors around the `error` + `meta` envelope, but you should still branch on route-specific status codes like `JOB_NOT_READY` and `JOB_FAILED`.
Canonical v2 envelope
Public v2 routes return a structured `error` object plus a `meta` object carrying the request ID and API version.
{
"error": {
"code": "BAD_REQUEST",
"message": "Request validation failed.",
"details": [
{
"path": "query",
"message": "String must contain at least 3 character(s)"
}
]
},
"meta": {
"request_id": "search-v2-demo-1",
"api_version": "v2"
}
}Job-state specific errors
Async job result endpoints use the same envelope, but add route-specific codes like `JOB_NOT_READY` and `JOB_FAILED`.
{
"error": {
"code": "JOB_NOT_READY",
"message": "Job has not completed yet.",
"details": {
"job_id": "d61d9b95-06f8-47a8-a409-77d9ee5756d1",
"status": "running"
}
},
"meta": {
"request_id": "0867f1dc-70b9-4d4a-9b01-0efc8f4d6720",
"api_version": "v2"
}
}Important caveat
Treat job creation, job polling, and job result endpoints as distinct contracts even though they share the same envelope shape.