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.
v1 API removed
The legacy /api/v1/* API has been removed. Existing v1 API keys were deleted as part of the cutover. Create a new API key and use the v2 endpoints documented here.
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 company lookup endpoint for fetching company records by website/domain or by company name.
/api/v2/foundersFounder discovery endpoint that accepts a website plus optional hints and returns the standard v2 response envelope.
/api/v2/competitor-analysesCompetitor analysis endpoint with the standard v2 response envelope.
/api/v2/vc-backability-checksClassify startup VC-backability synchronously for up to 50 websites or asynchronously for 51-500 websites.
VC Backability Jobs
Recover, poll, and fetch results for VC backability batches above 50 websites.
/api/v2/vc-backability-checksList recent VC backability async jobs owned by the authenticated API key.
/api/v2/vc-backability-checks/{job_id}Poll the status of an asynchronous VC backability job.
/api/v2/vc-backability-checks/{job_id}/resultFetch the completed result for an asynchronous VC backability job.
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. The removed legacy `/api/deep-research` route now returns `410 Gone`; use this route instead.
/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 `query_mode` lets you force company-name lookup or semantic discovery when you need predictable routing. The response comes back in the standard v2 envelope with interpreted search metadata, making 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",
"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
| 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. |
| query_mode | "auto" | "name" | "discovery" | null | No | Optional routing hint for lexical company-name lookup versus semantic discovery. |
| 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.lastRaisedAge | { operator?: "<" | ">" | "=" | "<=" | ">=" | "between"; value?: number | null; endValue?: number | null; unit?: "months" | "years"; includeMissing?: boolean } | No | Filter by relative time since the latest funding round. |
| 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.cityGeonameIds | number[] | { include?: number[]; exclude?: number[] } | No | Restrict results to canonical GeoNames city IDs, with optional include/exclude semantics. |
| filters.admin1Codes | string[] | { include?: string[]; exclude?: string[] } | No | Restrict results to canonical admin1 codes such as `US-CA`, with optional include/exclude semantics. |
| filters.regionSlugs | string[] | { include?: string[]; exclude?: string[] } | No | Restrict results to canonical metro region slugs such as `sf_bay_area`, with optional include/exclude semantics. |
| filters.industries | { values: string[]; operator?: "AND" | "OR" } | No | Match one or more normalized industry/category labels. `OR` is the default when omitted. |
| filters.includedInvestors | DatabaseInvestorFilterOption[] | No | Restrict results to startups backed by one or more selected investors. |
| filters.excludedInvestors | DatabaseInvestorFilterOption[] | No | Exclude startups backed by one or more selected investors. |
| 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. |
| data.request.query_mode_requested | string | null | No | Requested query routing mode, when provided. |
| data.request.query_mode_resolved | string | No | Resolved routing mode used by the search service. |
| data.request.used_lexical_lookup | boolean | No | Whether lexical company-name lookup ran for this request. |
| data.request.name_match_confidence | string | null | No | Present when lexical company-name lookup produced a ranked match. |
| 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",
"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
{
"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 }
},
"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
- 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.
/api/v2/company-lookupsCompany Lookups
Use this route to check a batch of company websites or company names against the StartupSeeker dataset in a single call. It is the fastest way to power CRM syncs, imports, enrichment queues, deduplication workflows, and exact brand-resolution flows without calling separate public endpoints.
The API preserves input order in `data.companies`, normalizes each submitted website or company name, and returns either a direct website hit or ranked lexical name matches. 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",
"names": ["Stripe", "OpenAI"],
"limit_per_name": 3
}Request Fields
| Name | Type | Required | Description |
|---|---|---|---|
| request_id | string | null | No | Optional client correlation ID. Maximum 128 characters. |
| websites | array<string> | null | No | Supply 1 to 100 company websites or domains for direct website/domain lookup. |
| names | array<string> | null | No | Supply 1 to 50 company names for lexical company-name lookup. |
| limit_per_name | number | null | No | Optional for `names` lookups only. Defaults to 5 and cannot exceed 10. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.companies | array<object> | No | Ordered lookup results in the same order as the submitted `websites` or `names` array. |
| data.companies[].lookup_kind | "website" | "name" | No | Indicates whether the item came from a website lookup or name lookup branch. |
| data.companies[].submitted | string | No | Original submitted website or company name string. |
| data.companies[].normalized | string | No | Normalized website/domain or normalized company name used for matching. |
| data.companies[].company | object | null | No | 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 | No | Present on name lookups. Contains `matched_alias`, `match_type`, `confidence`, `score`, and `company`. |
| data.companies[].matches | array<object> | No | Present on name lookups. Ranked lexical candidates for the submitted name. |
| 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",
"names": ["Stripe", "OpenAI"],
"limit_per_name": 3
}'Sample Response
{
"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,
"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, providing both `websites` and `names`, providing neither, invalid array sizes, 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
- 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.
/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. |
| company_name | string | null | No | Optional company name hint used when canonical lookup metadata is missing. |
| description | string | null | No | Optional description hint used when canonical lookup metadata is missing. |
| existing_founding_details | object | null | No | Optional founding year/location hints to preserve when evidence is weak. |
| seed_founders | array<object> | null | No | Optional structured founder seed inputs. |
| options.enrich_linkedin | boolean | No | Optional. Defaults to `true` so LinkedIn-based person linking stays enabled. |
| options.debug | boolean | No | Optional. Defaults to `false`. When `true`, the response includes founder decision debug data. |
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": [
{
"company": "Stripe",
"title": "Co-founder & CEO",
"duration": ""
}
],
"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/fundingFunding
Use this route when you want StartupSeeker to assemble a company funding profile from the funding extraction pipeline while receiving a stable public API contract. It runs the same core funding workflow used internally, then returns the canonicalized funding profile, execution counters, and optional supporting trace in the standard v2 envelope.
The response includes normalized website metadata, total funding summary fields, canonical rounds, linked investors, and source references after persistence. This makes it suitable for CRM enrichment, analyst tooling, and downstream pipelines that need funding data without relying on the legacy `/api/funding` response shape.
Request Shape
{
"request_id": "funding-v2-demo-1",
"website": "stripe.com",
"options": {
"mode": "standard",
"max_sources": 10
}
}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 funding history. |
| entity_id | string | null | No | Optional canonical startup entity UUID used to improve company-name resolution before the funding run. |
| options.mode | "standard" | "deep" | null | No | Optional execution mode kept for compatibility with the simplified funding pipeline. |
| options.max_sources | number | null | No | Accepted for compatibility with the simplified funding pipeline. |
| options.allow_database_only | boolean | null | No | Accepted for compatibility; ignored by the simplified funding pipeline. |
| options.include_leads | boolean | null | No | Accepted for compatibility; ignored by the simplified funding pipeline. |
| options.cost_optimized | boolean | null | No | Optional toggle that switches OpenRouter routing to the price-prioritized provider order (`deepinfra`, `novita`). |
| options.debug | boolean | null | No | Optional flag that includes the internal funding trace in the response. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.website.submitted | string | No | Original submitted website string. |
| data.website.normalized | string | No | Normalized absolute website URL used by the funding pipeline. |
| data.profile | object | No | Canonical funding profile loaded after persistence. |
| data.profile.total_funding_amount | number | null | No | Best available total funding amount in the profile currency. |
| data.profile.total_funding_currency | string | null | No | Currency code for `total_funding_amount`. |
| data.profile.total_funding_usd | number | null | No | Best available total funding amount normalized to USD. |
| data.profile.total_rounds | number | null | No | Best available count of canonical funding rounds. |
| data.profile.rounds | array<object> | No | Canonical funding rounds with amounts, dates, valuations, investors, linked investors, and sources. |
| data.profile.rounds[].investors[].role | "lead" | "co_lead" | "participant" | "unknown" | null | No | Optional investor role per round. Lead and co-lead roles are present only when explicitly supported by source evidence. |
| data.profile.linked_investors | array<object> | No | Unique linked investors that could be resolved from the canonical investor graph, including canonical logo URLs when available. |
| data.profile.total_funding_sources | array<object> | No | Sources supporting the total-funding assertion when available. |
| data.sources | array<object> | No | Funding sources cited by the simplified SERP-to-LLM pipeline. |
| data.counters | object | No | Simplified funding pipeline execution counters. |
| data.used_custom_context | boolean | No | Always `false` on the simplified funding route. |
| data.context_truncated | boolean | No | Always `false` on the simplified funding route. |
| data.trace | object | null | No | Only present when `options.debug` is `true`. |
| meta | object | No | Standard v2 metadata envelope with rate-limit and credit state. |
Sample Request
curl -X POST https://startup-seeker.com/api/v2/funding \
-H "Authorization: Bearer ssk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"request_id": "funding-v2-demo-1",
"website": "stripe.com",
"options": {
"mode": "standard",
"max_sources": 10
}
}'Sample Response
{
"data": {
"website": {
"submitted": "stripe.com",
"normalized": "https://stripe.com/"
},
"profile": {
"entity_id": "entity-stripe",
"startup_id": "startup-stripe",
"canonical_domain": "stripe.com",
"total_funding_amount": 2200000000,
"total_funding_currency": "USD",
"total_funding_usd": 2200000000,
"total_rounds": 8,
"latest_round_type": "Series I",
"latest_round_date": "2023-03-15",
"total_funding_sources": [
{
"url": "https://stripe.com/newsroom",
"snippet": "Stripe has raised more than $2.2 billion to date."
}
],
"rounds": [
{
"round_id": "round-stripe-series-i",
"round_type": "Series I",
"round_variant": null,
"announced_at": "2023-03-15",
"date_source": "explicit",
"closed_at": null,
"amount_original": 694000000,
"currency_original": "USD",
"amount_usd": 694000000,
"valuation_pre_money_amount_original": null,
"valuation_pre_money_currency_original": null,
"valuation_pre_money_usd": null,
"valuation_post_money_amount_original": 50000000000,
"valuation_post_money_currency_original": "USD",
"valuation_post_money_usd": 50000000000,
"valuation_amount_original": null,
"valuation_currency_original": null,
"valuation_usd": null,
"edited_by_actor_type": null,
"status": "confirmed",
"investors": [
{
"name": "Thrive Capital",
"website": null,
"role": "lead"
},
{
"name": "Founders Fund",
"website": null,
"role": "participant"
}
],
"linked_investors": [],
"sources": [
{
"url": "https://stripe.com/newsroom",
"snippet": "Thrive Capital led Stripe's latest financing round."
}
]
}
],
"linked_investors": []
},
"sources": [
{
"url": "https://stripe.com/newsroom",
"doc_type": "press_release",
"about_score": 0.99,
"scraped": false,
"windows": 0
}
],
"counters": {
"serp_requests": 1,
"serp_candidates": 12,
"model_rounds": 6,
"persisted_events": 3,
"events": 3,
"leads": 0
},
"used_custom_context": false,
"context_truncated": false
},
"meta": {
"request_id": "funding-v2-demo-1",
"api_version": "v2",
"duration_ms": 2840,
"credits": { "charged": 5, "remaining": 94 }
}
}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, invalid `website`, or invalid funding options. |
| 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 | Funding processing or canonical persistence failed unexpectedly. |
| 502 | BAD_GATEWAY | An upstream funding provider failed. |
Implementation Notes
- This is the preferred public funding endpoint for new integrations.
- The legacy `/api/funding` route remains available for internal/browser flows and compatibility, but it does not use the standard v2 metadata envelope.
- Set `options.debug: true` only when you explicitly need the internal funding trace in the response.
- 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
- 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.
/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, a descriptive payload with short company descriptions, and a fuller response with more supporting output. Requests with 51 to 500 websites return an async job with `poll_url` and `result_url`, while requests with 1 to 50 websites return results synchronously.
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 1-50 websites for a sync response, or 51-500 to create an async job. |
| website | string | null | No | Single-site shortcut when not using `websites`. |
| response_detail | "minimal" | "descriptive" | "full" | null | No | 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
| Name | Type | Required | Description |
|---|---|---|---|
| data.count | number | No | Number of entries returned in `data.results`. |
| data.results | array<object> | No | One item per submitted website, preserving order. Row failures are returned inline with `status: "error"`. |
| data.summary | object | No | Counters for row errors, canonical hits, active queue hits, cache hits, and live classifications. |
| 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": [
{
"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,
"credits": { "charged": 0, "remaining": 99 }
}
}Important Headers
| Header | Description |
|---|---|
| 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-Credits-Remaining | Remaining API credits after the request finalizes. |
Common Errors
| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Request omitted both `website` and `websites`, exceeded 500 websites, or validation failed. |
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 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. |
Implementation 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.
/api/v2/vc-backability-checksList VC Backability Jobs
Use this listing endpoint to recover VC backability jobs created by the current API key. It is useful when a client did not persist the original `202` response or needs to rebuild a queue view.
Each returned job includes relative `poll_url` and `result_url` values. Prefix those paths with your API base URL, poll while the job is queued or running, and fetch the result once it succeeds.
Request Shape
GET /api/v2/vc-backability-checks?status=running&limit=10&request_id=vc-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[].progress | number | No | Coarse job progress percentage. |
| 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/vc-backability-checks?status=running&limit=10&request_id=vc-v2-demo-1" \
-H "Authorization: Bearer ssk_live_your_api_key"Sample Response
{
"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,
"credits": { "charged": 0, "remaining": 99 }
}
}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 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.
/api/v2/vc-backability-checks/{job_id}VC Backability Job Status
Use this route to monitor a VC backability batch after a 51 to 500 website request returned `202`. It reports lifecycle state, coarse progress, timestamps, and response metadata without returning the full result body.
Poll this endpoint while the job is `queued` or `running`, then fetch `GET /api/v2/vc-backability-checks/{job_id}/result` after the job reaches `succeeded`.
Request Shape
GET /api/v2/vc-backability-checks/{job_id}Request Fields
| Name | Type | Required | Description |
|---|---|---|---|
| job_id | string | Yes | Job identifier returned by the async 202 response or list endpoint. |
Response Fields
| Name | Type | Required | Description |
|---|---|---|---|
| data.job_id | string | No | Job identifier. |
| data.job_type | string | No | Job type, currently `vc_backability_check`. |
| data.status | string | No | Job lifecycle state: `queued`, `running`, `succeeded`, or `failed`. |
| data.progress | number | No | Coarse job progress percentage. |
| data.response_meta | object | No | Counters such as website count, row errors, cache hits, and live classifications. |
| meta.job | object | No | Job metadata mirrored into the standard v2 meta envelope. |
Sample Request
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
{
"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,
"credits": { "charged": 0, "remaining": 99 },
"job": {
"id": "58466a0d-4a9a-41b2-8753-5d30749c5d4d",
"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.
- When `data.status` becomes `succeeded`, call `GET /api/v2/vc-backability-checks/{job_id}/result` for the same job ID.
/api/v2/vc-backability-checks/{job_id}/resultVC Backability Job Result
Use this route to fetch the final output for an async VC backability batch. The completed result is nested under `data.result` and uses the same `count`, `results`, and `summary` fields as the synchronous response.
`409 JOB_NOT_READY` means the job is still processing. `422 JOB_FAILED` means the batch hit a systemic failure; row-level scrape/classification failures are still returned inside `data.result.results` when the job itself succeeds.
Request Shape
GET /api/v2/vc-backability-checks/{job_id}/resultRequest Fields
| Name | Type | Required | Description |
|---|---|---|---|
| job_id | string | Yes | Job identifier returned by the async 202 response or list 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.count | number | No | Number of submitted entries. |
| data.result.results | array<object> | No | One VC backability row per submitted website, preserving order. |
| data.result.summary | object | No | Counters for classified rows, row errors, canonical hits, active queue hits, cache hits, and live classifications. |
| meta | object | No | Standard v2 metadata envelope. |
Sample Request
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
{
"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,
"credits": { "charged": 0, "remaining": 99 },
"job": {
"id": "58466a0d-4a9a-41b2-8753-5d30749c5d4d",
"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 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.
/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
- 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 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",
"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. |
| 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`. |
| 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",
"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 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 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.