Raw OpenAPI specification (YAML - inlined for the scrapers that need
it)
openapi: 3.2.0
info:
title: Riveter API
description: |
## Overview
The Riveter API lets you **enrich** data, **build datasets**, **scrape** webpages, quickly **search** the web, and **extract** data from complicated websites programmatically.
- **An enrichment** takes rows of input data and fills in new columns using AI, web searches, web scrapes, and other tools. For example, given a list of companies, an enrichment can pull their tech stack, analyze their pricing structure, determine what security tools they use, and much more.
- **A dataset** is a collection of rows — companies, people, products, URLs, or anything else you want to work with. You can build one from a natural-language prompt or a structured spec, and Riveter will generate the rows for you.
- **A scrape** lets you turn a URL into easily parseable text.
- **A quick_search** lets you quickly web search a query, and pull structured results with urls, titles, and snippets — synchronously, in one request.
- **A search_agent call** asks one question and gets one AI-researched answer back — the same agent loop that fills a single enrichment cell, without setting up an enrichment.
Every asynchronous operation returns a **run** with an id like `run_...`, and every run — enrichment, dataset build, extraction, or search — is tracked, fetched, and stopped the same way through `/runs`.
## The run lifecycle
Every kickoff endpoint (`/enrich`, `/datasets`, `/extractions/{id}/runs`, ...) returns `201 Created` with a **run**. From there:
| Request | What it does |
|---------|--------------|
| `GET /runs/{id}` | Check status (`pending` → `enqueued` → `processing` → `success` or `stopped`) |
| `GET /runs/{id}/result?wait=30` | Fetch the output. `wait` long-polls up to 50 seconds; `output` is `null` until the run finishes |
| `POST /runs/{id}/stop` | Stop a run early |
Prefer webhooks over polling: pass `webhook_url` on kickoff and Riveter POSTs the results to you when the run finishes. If you poll, an interval of 10-20 seconds is plenty.
Every kickoff and every `/runs` endpoint returns the same **run** — see the [Runs](#tag/runs) section for the full shape.
## Legacy endpoints
This is the second generation of the Riveter API. The first generation used verb-style paths (`/run_new_enrichment`, `/run_status`, `/run_data`, `/build_dataset`, ...). Those endpoints are legacy: they keep working at the same paths, but new work should use this API. Every endpoint documented here is current — none are legacy.
Common legacy → current mappings: `/run_new_enrichment` → `/enrich`, `/run_enrichment` → `/enrich` + `enrichment_id`, `/run_status` → `/runs/{id}`, `/run_data` → `/runs/{id}/result`, `/build_dataset` → `/datasets`, `/monitor_enrichment` → `/monitors`. The full table is in the [legacy API docs](./openapi.legacy.yaml) ("Migrating to the current API").
Legacy responses say so themselves: the body carries `"legacy": { "notice", "successor", "docs" }` and the response has `Deprecation: true` plus a `Link` header (`rel="successor-version"`). Endpoints documented here never carry them.
## Webhooks
Pass `webhook_url` in the JSON body when starting a run and Riveter POSTs the full results to your URL when it finishes (events: `run.completed`, `run.stopped`, `run.finished`). Dataset builds take `dataset_webhook_url`. Failed deliveries are retried up to 2 times; your endpoint should return a 2xx.
The webhook payload shape is shared with the legacy API — see the [legacy API docs](./openapi.legacy.yaml) ("Webhooks" section) for the full payload reference.
## Authentication
All endpoints require an API key via the Authorization header:
```
Authorization: Bearer YOUR_API_KEY
```
[Get an API key here](https://app.riveterhq.com/settings/api)
## Rate limiting
Default: 30 requests per minute per endpoint group. Responses include `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers. Row caps: up to 10,000 rows per `/enrich` request with `enrichment_id`, 1,000 rows with an inline config (`output` or `prompt` + `attributes`).
## MCP server
Use Riveter from Claude, ChatGPT, Cursor, or any MCP-compatible AI assistant. Pick one of the two ways to connect.
### Hosted server (claude.ai, Claude Desktop, Cowork, mobile, ChatGPT, Claude Code, Cursor)
Add `https://mcp.riveterhq.com/mcp` as a custom connector, then click Connect. A browser window opens on Riveter: sign in and click **Allow**. That creates an API key for the connection in [Settings → API keys](https://app.riveterhq.com/settings/api); revoke it there to disconnect. Nothing runs on your machine.
**Claude Code:**
```bash
claude mcp add --transport http riveter https://mcp.riveterhq.com/mcp
```
Then run `/mcp` inside Claude Code and choose Authenticate.
**Cursor** — paste into your MCP config, then click Connect in the MCP settings:
```json
{
"mcpServers": {
"riveter": {
"url": "https://mcp.riveterhq.com/mcp"
}
}
}
```
If your client cannot open a browser, pass an [API key](https://app.riveterhq.com/settings/api) directly as a request header: `Authorization: Bearer YOUR_API_KEY` (Claude Code: `--header "Authorization: Bearer YOUR_API_KEY"`; Cursor: `"headers": { "Authorization": "Bearer YOUR_API_KEY" }`).
### Local server (npx)
Runs on your machine and needs Node.js and an [API key](https://app.riveterhq.com/settings/api). Use it when your client cannot reach remote servers.
**Claude Code:**
```bash
claude mcp add riveter \
--env RIVETER_API_KEY=YOUR_API_KEY \
-- npx -y --prefer-online riveter-mcp-server@latest
```
**Codex:**
```bash
codex mcp add riveter \
--env RIVETER_API_KEY=YOUR_API_KEY \
-- npx -y --prefer-online riveter-mcp-server@latest
```
**Cursor / Windsurf / Claude Desktop** — paste into your MCP config:
```json
{
"mcpServers": {
"riveter": {
"command": "npx",
"args": ["-y", "--prefer-online", "riveter-mcp-server@latest"],
"env": {
"RIVETER_API_KEY": "YOUR_API_KEY"
}
}
}
}
```
Both servers expose every API endpoint as a tool, with full descriptions and typed parameters. No setup beyond the API key.
_**Updating:** The hosted server reloads the API definition every 10 minutes on its own. The local (npx) server loads it once when your client starts it, so after an API update restart your AI client to get the new tools. You never need to remove and re-add the server._
## Official SDKs
Prefer a typed client over raw HTTP — the SDKs handle auth, retries (429s and transient GET failures), the `wait` long-poll, polling until a run finishes (`wait_for_result`), and pagination:
| Language | Install | Package |
|----------|---------|---------|
| TypeScript / JavaScript | `npm install riveter-sdk` | [riveter-sdk on npm](https://www.npmjs.com/package/riveter-sdk) |
| Python | `pip install riveter-sdk` | [riveter-sdk on PyPI](https://pypi.org/project/riveter-sdk/) (import `riveter`) |
| Ruby | `gem install riveter-sdk` | [riveter-sdk on RubyGems](https://rubygems.org/gems/riveter-sdk) (`require "riveter"`) |
| Go | `go get github.com/riveterhq/riveter-go` | [riveterhq/riveter-go](https://github.com/riveterhq/riveter-go) |
Every endpoint on this page shows the equivalent SDK call in all four languages next to the request example.
## Timestamps
All timestamps in responses are ISO 8601 strings with second precision and an explicit UTC offset (e.g. `2026-01-15T12:00:00Z`).
## Errors
Errors use real HTTP status codes and a uniform body:
```json
{ "error": { "type": "not_found", "message": "No run found with id run_..." } }
```
Common `type` values: `bad_request`, `not_found`, `forbidden`, `duplicate_run_key`, `insufficient_credits`, `credit_cap_exceeded`, `validation`, `not_implemented`.
One exception: authentication failures (`401`) come from the shared auth layer and use the legacy shape `{ "request_status": "error", "message": "...", "error_type": "unauthorized" }`.
## Credits: estimate before you spend
Every endpoint that spends credits (`/enrich`, `/datasets`, `/datasets/{id}/extend`, `/enrichments/{id}/datasets`, `/configured_datasets/{id}/build`, `/extractions`, `/extractions/{id}/runs`, `/quick_search`, `/search_agent`, `/scrape`) accepts two optional fields:
- `dry_run: true` — validate the request and return a [credit estimate](#model/dryrunresult) without creating or charging anything. The response is `200` with `dry_run: true`, the `type` of run that would start, and a `credit_detail` that includes `credits_remaining` and `sufficient_credits`.
- `max_credits` — a ceiling. When the estimate's `maximum` exceeds it, the request is refused with `422 credit_cap_exceeded` and nothing is charged.
Every run also carries `credit_detail` (`{ estimate: { minimum, maximum, charged_upfront }, credits_used }`) on kickoff and on `GET /runs/{id}`. `maximum` is the contract: a run never charges more than it. `minimum` is `0` when cells can short-circuit (blank inputs, `run_when` rules, already-filled cells) or when unfound dataset rows are refunded. `charged_upfront` is `true` for dataset builds and extractions (taken at kickoff, refunded later where applicable) and `false` for enrichments and searches (accrued as work completes).
# MCP server `instructions`. Deliberately short (~200 tokens max, enforced by
# mcp-server/test/spec-contract.test.mjs): every MCP client injects this into
# the model's context at session start, and some (Codex) repeat it once per
# tool — with 26 tools, 2K tokens here became ~50K tokens of overhead. It
# competes with the user's own context on every turn. Keep only cross-tool
# behavior; anything about one tool belongs in that operation's description,
# and setup / auth / SDK / migration text belongs in `description` above (the
# docs site), which MCP clients never see.
x-mcp-instructions: |
Riveter builds datasets (rows), enriches rows with new columns, scrapes pages, searches, and answers research questions.
Async tools (`enrich`, `build_dataset`, `run_extraction`, ...) return a run `id` (`run_...`). Call `get_run_result` with `wait: 50` to long-poll for `output`, or `get_run` every 10–20 s at most. Prefer `webhook_url`.
`scrape`, `quick_search`, `search_agent` are synchronous. Monitors re-run a saved enrichment on a schedule.
Prefer `enrich` with a saved `enrichment_id`. `list_enrichments` is compact; `get_enrichment` has the columns.
Paid tools accept `dry_run: true` (estimate only) and `max_credits` (refused if the estimate is higher).
Call `help` with a topic before writing an `output` column spec or when an error names a limit.
Id prefixes: `run_`, `enr_`, `ds_`, `ext_`, `mon_`. Auth comes from the MCP connection.
# Reference docs served by the MCP `help` tool, one topic per key (the keys
# become the tool's `topic` enum). Loaded only when the agent calls `help`,
# so a topic may be long — but keep each one single-purpose. JSON examples
# here are validated by spec/lib/external_api/mcp_help_examples_spec.rb
# against the real /v1/enrich validator and by mcp-server/test against the
# schema enums, so they cannot drift from the API.
x-mcp-help:
overview: |
# Riveter, in one page
Riveter turns lists into researched tables.
- **Dataset build** (`build_dataset`) — finds rows from a description ("top 50 US SaaS companies"). Returns identifier columns only (e.g. Company + Website). It does not research anything about the rows.
- **Enrichment** (`enrich`) — takes rows (inline `input` or a `dataset_id`) and fills in new columns using an AI agent per cell, with web search, scraping, PDFs, LinkedIn tools, and more. This is where research happens.
- **Scrape** (`scrape`) — one URL → clean text. Synchronous.
- **Quick search** (`quick_search`) — one query → titles, URLs, snippets. Synchronous.
- **Search agent** (`search_agent`) — one question → one AI-researched answer. Synchronous. Same agent that fills one enrichment cell.
- **Extraction** (`create_extraction` then `run_extraction`) — recipe for pulling structured records out of a hard website (pagination, filters). Discovery is a paid, one-time step per site.
- **Monitor** (`create_monitor`) — re-run a saved enrichment daily / weekly / monthly, optionally with diff webhooks.
**Runs.** Every async kickoff returns a run (`run_...`). `get_run` = status, `get_run_result` = status + `output` (`wait: 50` long-polls), `stop_run` = cancel. Statuses: `pending` → `enqueued` → `processing` → `success` | `stopped`.
**Typical flows**
1. User has a list, wants columns → `enrich` (saved `enrichment_id` if one exists; else `prompt` + `attributes`; else a full `output` spec — see `help(enrich_modes)`).
2. User has no list → `build_dataset` (rows) → `enrich` with the `dataset_id` (research). Or `build_dataset` with `auto_run_enrichment: true` to chain both.
3. Repeatable research → build and tune the enrichment once, then always run it by `enrichment_id`.
**Money.** Everything except `get_*` / `list_*` spends credits. Add `dry_run: true` to any paid call to see the estimate first; add `max_credits` to refuse anything above a ceiling. See `help(credits)`.
enrich_modes: |
# `enrich`: the three ways to say what columns to add
Rows come from exactly one of `input` (inline) or `dataset_id` (a completed build, `ds_...`). Column config comes from exactly one of the three below.
**`input` shape** — columnar. Keys are headers, values are equal-length string arrays, one position = one row:
```json
{ "input": { "Company": ["Apple", "Google"], "Website": ["apple.com", "google.com"] } }
```
## 1. `enrichment_id` — run a saved enrichment (preferred)
Fixed, tested config → most consistent results. Up to 10,000 rows. `input` headers must match the enrichment's source columns (`get_enrichment` shows them under `input`). Find ids with `list_enrichments`.
```json
{ "enrichment_id": "enr_...", "input": { "Company Name": ["Acme Corp", "Tech Solutions Inc"] } }
```
## 2. `prompt` + `attributes` — auto-generated config
Describe the job and name the output columns (max 20). Riveter drafts the full column config, then runs. No setup, but the config is regenerated every call, so results vary run to run. Up to 1,000 rows. Good for one-offs and for drafting an enrichment you will then save and tune.
```json
{
"prompt": "For each B2B SaaS company, find the CEO, employee count, and primary industry.",
"attributes": ["CEO", "Employee Count", "Industry"],
"input": { "Company": ["Apple", "Google"] }
}
```
Put qualifiers in the prompt (industry, geography, entity type) — each cell agent sees only its prompt and its row.
## 3. `output` — full column spec
Exact control: per column you set the prompt, contexts, tools, format, and `run_when`. Up to 1,000 rows. See `help(column_config)` for every field and `help(examples)` for complete recipes.
```json
{
"input": { "Company": ["Apple", "Google"] },
"output": {
"Employee Count": { "prompt": "Find the current number of employees at this company.", "contexts": ["Company"], "format": "number" },
"CEO": { "prompt": "Find the CEO's full name.", "contexts": ["Company"] }
}
}
```
## Save, then run by id
To make mode 2 or 3 repeatable: create the enrichment in the Riveter UI (or from a finished dataset with `create_enrichment`), tune it with `update_enrichment`, then run it with mode 1.
## After kickoff
The response is a run. `get_run_result({ id, wait: 50 })` until `output` is non-null, or pass `webhook_url` and stop polling. `output` is columnar: `{ "Column": [{ "value": ... }, ...] }`, one entry per input row.
column_config: |
# Output column config (the values in `output: { "<Header>": { ... } }`)
Two modes per column. Never mix their fields.
## Agent mode — an AI agent researches each cell
| field | notes |
|---|---|
| `prompt` | Required. Self-contained instruction. The cell agent sees ONLY this prompt and the row's context values — name the entity type and any qualifiers ("this YC-backed fintech"). |
| `contexts` | Required, non-empty. Headers whose values are given to the agent: input columns and/or EARLIER output columns. |
| `tools` | Optional. Default `["web_search", "scrape", "pdf", "http_request"]`. Add `"image"` for image analysis. Use `["scrape"]` to force "only read this URL". `[]` = no tools (pure reasoning over contexts). |
| `format` | Optional. `text` (default), `number`, `url`, `email`, `tag`, `date`, `json`, `boolean`. See `help(formats)`. |
| `format_details` | Per-format options (tag options, JSON schema, ...). See `help(formats)`. |
| `run_when` | Per-row gate. See `help(run_when)`. |
| `max_tool_calls` | Optional cap on tool calls per cell (default 10). |
## Tool-only mode — one tool, no agent loop; cheaper and deterministic
Set `tool` plus that tool's params. **Do not send `prompt`/`contexts` for tools that don't take them** — dependencies are inferred from param values that match a column header.
```json
{ "Homepage Text": { "tool": "scrape", "url": "Website" } }
```
When a param value equals a column header it is per-row (that row's value); otherwise it is a static string. Tools and params: `help(tools)`.
`run_when` / `run_when_config` work here too — gate an expensive tool call on an earlier column's value. Rule columns become dependencies automatically; do not add `contexts`. See `help(run_when)`.
## Ordering and dependencies
Columns run in dependency order, and the order you write them matters: a column may only reference columns defined BEFORE it (via `contexts`, via tool params / `args` that name a column, or via a tool-only column's `run_when_config` rules). Pattern: extraction → analysis → synthesis → scoring.
## Rules that save credits and avoid bad data
- Fetch a source once. One `scrape` / LinkedIn / PDF column, then agent columns with that column in `contexts` to extract pieces. Never three columns scraping the same page.
- Group related fields from one source into one `json` column instead of N agent columns.
- Prefer `text` / `number` / `boolean` over `json` when one value is enough.
- For URL columns add "Only return a real URL found via search or scrape — do not guess."
- Do not tell the agent to return null / N/A when not found — Riveter emits a "not found" value itself.
- Chained columns: set `run_when: "any_filled"` so rows where the upstream column found nothing are skipped for free.
- Input headers and output headers must all be unique.
run_when: |
# `run_when` — skip rows, chain columns
A per-row gate checked before the cell runs, for agent columns AND tool-only columns. A skipped cell stays empty and costs **0 credits**.
| value | runs when |
|---|---|
| `always` (default) | every row |
| `any_filled` | at least one of the column's dependencies has a value ("not found" counts as empty). Dependencies = `contexts` for agent columns; column-mapped tool params for tool-only columns. The right default for any column that depends on an earlier output column. |
| `all_filled` | every dependency has a value. Rarely wanted — one "not found" silently skips the row. |
| `dynamic` | the `run_when_config` rules match. Use to branch on an earlier output's VALUE. |
## `run_when_config`
```json
{ "match_mode": "all", "rules": [{ "column": "CEO", "condition": "is_not_empty" }] }
```
- `match_mode`: `all` (default) or `any`.
- `rules[].column`: header of an input column or an EARLIER output column.
- Agent column: **it must also be in this column's `contexts`** — the row's cell value is only loaded for context columns; a rule on a non-context column always sees empty.
- Tool-only column: no `contexts` needed — every rule column is added to the column's dependencies automatically (it must still be defined BEFORE this column).
- `rules[].condition`: `is_empty`, `is_not_empty` (no `value`); `text_contains`, `text_does_not_contain`, `text_starts_with`, `text_ends_with` (case-insensitive, need `value`); `text_is_exactly` (exact, case-sensitive, needs `value`).
## Example — skip an expensive tool-only lookup unless a cheap check passes
```json
{
"input": { "LinkedIn URL": ["https://linkedin.com/in/example"] },
"output": {
"Profile": { "tool": "linkedin_person_profile", "url": "LinkedIn URL" },
"Seniority": { "prompt": "Classify this person's seniority from their profile.", "contexts": ["Profile"], "format": "tag", "format_details": { "options": ["ic", "manager", "executive"] }, "run_when": "any_filled" },
"Work Email": {
"tool": "linkedin_person_contact",
"url": "LinkedIn URL",
"run_when": "dynamic",
"run_when_config": { "rules": [{ "column": "Seniority", "condition": "text_is_exactly", "value": "executive" }] }
}
}
}
```
`Work Email` only spends credits on rows where `Seniority` is `executive`.
## Example — look up an email only when a CEO was found; write notes only for enterprise companies
```json
{
"input": { "Company": ["Stripe", "Acme Corp"] },
"output": {
"CEO": { "prompt": "Find the CEO's full name.", "contexts": ["Company"] },
"Company Size": { "prompt": "Classify this company's size.", "contexts": ["Company"], "format": "tag", "format_details": { "options": ["startup", "mid-market", "enterprise"] } },
"CEO Email": {
"prompt": "Find a work email address for this CEO.",
"contexts": ["Company", "CEO"],
"format": "email",
"run_when": "dynamic",
"run_when_config": { "rules": [{ "column": "CEO", "condition": "is_not_empty" }] }
},
"Enterprise Notes": {
"prompt": "Summarize this company's enterprise offering, pricing model, and notable enterprise customers.",
"contexts": ["Company", "Company Size"],
"run_when": "dynamic",
"run_when_config": { "rules": [{ "column": "Company Size", "condition": "text_is_exactly", "value": "enterprise" }] }
}
}
}
```
With `prompt` + `attributes`, Riveter sets `run_when` for you. To change gating on a saved enrichment, send the column's `run_when` / `run_when_config` through `update_enrichment`.
formats: |
# `format` and `format_details`
Only set a format when the value has a natural type. `text` is the default and is fine for prose.
| format | value | `format_details` keys |
|---|---|---|
| `text` | free text | — |
| `number` | numeric | `decimal_places` (0–9), `currency_code` ("USD"), `commas` (bool), `percentage` (bool). `currency_code` and `percentage` are mutually exclusive. |
| `url` | one URL | — |
| `email` | one email | — |
| `boolean` | true/false | — |
| `tag` | one of a fixed set | `options` (required, string array), `allow_multiple` (bool), `descriptions` (map option → meaning) |
| `date` | a date | `iso_8601: true`, or tokens `month` (M, MM, MMM, MMMM), `day` (D, DD, Do), `year` (YYYY, YY), `delimiter` |
| `json` | structured object | `schema` (JSON Schema object) and/or `description` (natural-language shape) |
## `tag` example
```json
{ "Industry": { "prompt": "Classify this company's primary industry.", "contexts": ["Company", "Website"], "format": "tag", "format_details": { "options": ["SaaS", "Fintech", "Healthcare", "Other"], "descriptions": { "Other": "Anything not covered by the other tags" } } } }
```
## `json` schema rules
- Root `type` must be `object` (or `array`). Riveter adds `additionalProperties: false` and the full `required` array for you (strict mode) — you may include them, but every property will be required either way.
- Optional fields: make them nullable, `"type": ["string", "null"]`.
- Add `minLength` to text fields you care about (reasoning ~50, descriptions ~20, names ~2) — `required` alone allows `""`.
- Keep it flat: ≤ 5–6 properties per column. Split otherwise.
- Not supported: `$ref`, `oneOf`, `allOf`, `not`, nesting > 10 levels. `anyOf` is supported. Do not use `maxItems` on arrays (it truncates data).
```json
{
"Pricing": {
"prompt": "Find this product's pricing. Return the plan names, monthly price in USD, and whether a free tier exists.",
"contexts": ["Product", "Website"],
"format": "json",
"format_details": {
"schema": {
"type": "object",
"properties": {
"plans": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "minLength": 2 }, "monthly_usd": { "type": ["number", "null"] } }, "required": ["name", "monthly_usd"], "additionalProperties": false } },
"has_free_tier": { "type": "boolean" },
"source_url": { "type": ["string", "null"] }
},
"required": ["plans", "has_free_tier", "source_url"],
"additionalProperties": false
}
}
}
}
```
tools: |
# Tools
## Agent-mode `tools` (the agent decides when to call them)
`web_search`, `scrape`, `pdf`, `image`, `http_request`, `check_urls`, `text_search_pdf`. Default when omitted: `["web_search", "scrape", "pdf", "http_request"]`. Each cell has a budget of tool calls (`max_tool_calls`, default 10). Tools cost credits inside the cell's price; keep the default set unless you have a reason.
## Tool-only `tool` (one deterministic call per row, no agent)
Param values that equal a column header are per-row; anything else is static.
| `tool` | required params | optional | returns |
|---|---|---|---|
| `scrape` | `url` | `proxy_country_code`, `wait_longer`, `skip_cache` | page text |
| `web_search` | `query` | `date_start`, `date_end` (YYYY-MM-DD) | results with titles/urls/snippets |
| `pdf` | `url`, `prompt` | — | answer extracted from the PDF |
| `image` | `url`, `prompt` | — | answer about the image |
| `http_request` | `url` | `method` (GET/POST), body fields | raw HTTP response (public APIs only) |
| `code` | `code` | `args` | return value of your JS. Free (0 credits). Sandbox, no network. |
| `linkedin_person_profile` | `url` (linkedin.com/in/<slug>) | — | profile data |
| `linkedin_person_contact` | `url` | — | emails (waterfall) |
| `linkedin_person_contact_lite` / `linkedin_person_phones_lite` | `url` | — | cheaper email / phone lookups |
| `linkedin_person_posts` / `_comments` / `_reactions` / `_activities` | `url` | — | activity |
| `linkedin_company_profile` / `_data` / `_job_count` / `_depth_chart` / `_posts` | `url` (linkedin.com/company/<slug>) | — | company data |
| `linkedin_post_comments` / `linkedin_post_reactions` | `post_url` | `reaction_type` | post engagement |
## `code` tool
`code` is a JavaScript function body; read inputs via `args.<name>`. `args` maps names to column headers (per-row) or static strings. Dependencies come from `args`, so a code column must be defined after the columns it reads.
```json
{
"Domain": { "tool": "code", "code": "try { return new URL(args.url).hostname.replace(/^www\\./, '') } catch (e) { return null }", "args": { "url": "Website" } },
"Revenue per Employee": { "tool": "code", "code": "const r = parseFloat(args.revenue) || 0; const e = parseInt(args.employees) || 1; return (r / e).toFixed(2)", "args": { "revenue": "Annual Revenue", "employees": "Employee Count" } }
}
```
## LinkedIn: always resolve the URL first
Input columns called "LinkedIn URL", "Person URL", "Website" are usually NOT canonical LinkedIn URLs. Never pipe them straight into a `linkedin_*` tool. Add an agent column that returns the exact `https://linkedin.com/in/<slug>` (or `/company/<slug>`) URL, then point the tool-only column at that column. One resolver per entity type. Full recipe: `help(examples)` → "Person enrichment via LinkedIn".
## When to use which mode
- Known URL + one tool (read a PDF, scrape a page, LinkedIn lookup) → tool-only.
- Deterministic transform of existing columns → tool-only `code`.
- Needs searching, reasoning, or more than one tool → agent mode.
examples: |
# Complete `enrich` payloads (copy, edit headers, run)
Each block is a full tool call. Prefer these shapes over inventing new ones.
## Company basics
```json
{
"input": { "Company": ["Stripe", "Notion"], "Website": ["stripe.com", "notion.so"] },
"output": {
"Legal Name": { "prompt": "Find this company's registered legal name.", "contexts": ["Company", "Website"], "run_when": "any_filled" },
"HQ City": { "prompt": "Find the city of this company's headquarters.", "contexts": ["Company", "Website"], "run_when": "any_filled" },
"Employee Count": { "prompt": "Find the current number of employees.", "contexts": ["Company", "Website"], "format": "number", "run_when": "any_filled" },
"Annual Revenue": { "prompt": "Find the most recent annual revenue in USD.", "contexts": ["Company", "Website"], "format": "number", "format_details": { "currency_code": "USD" }, "run_when": "any_filled" }
}
}
```
## Lead scoring (extract → analyze → score)
```json
{
"input": { "Company": ["Acme Agency"], "Website": ["acme.agency"] },
"output": {
"Services Offered": { "prompt": "List the services this agency offers, from its website.", "contexts": ["Company", "Website"], "tools": ["scrape", "web_search"] },
"Is Webflow Agency": { "prompt": "Based on the services listed, is this primarily a Webflow design/development agency?", "contexts": ["Company", "Services Offered"], "tools": [], "format": "boolean", "run_when": "any_filled" },
"ICP Score": {
"prompt": "Score fit for our ICP (Webflow agencies, 5–50 staff, US/EU). Return score 0–100, a segment, and reasoning.",
"contexts": ["Company", "Services Offered", "Is Webflow Agency"],
"tools": [],
"format": "json",
"format_details": { "schema": { "type": "object", "properties": { "score": { "type": "number" }, "segment": { "type": "string", "enum": ["ideal", "possible", "poor"] }, "reasoning": { "type": "string", "minLength": 50 } }, "required": ["score", "segment", "reasoning"], "additionalProperties": false } },
"run_when": "any_filled"
}
}
}
```
## Person enrichment via LinkedIn (resolver → tool-only → synthesis)
```json
{
"input": { "Person Name": ["Tobias Lütke", "Patrick Collison"], "Qualifying Information": ["CEO of Shopify", "CEO of Stripe"] },
"output": {
"LinkedIn URL": {
"prompt": "Find this person's canonical LinkedIn profile URL in the form https://linkedin.com/in/<slug>. Only return a real URL found via search or scrape — do not guess or fabricate one.",
"contexts": ["Person Name", "Qualifying Information"],
"tools": ["web_search", "scrape"],
"format": "url",
"run_when": "any_filled"
},
"LinkedIn Data": { "tool": "linkedin_person_profile", "url": "LinkedIn URL" },
"Contact Data": { "tool": "linkedin_person_contact", "url": "LinkedIn URL" },
"Bio": { "prompt": "Write a two-sentence professional bio of this person from the details provided.", "contexts": ["Person Name", "Qualifying Information", "LinkedIn Data"], "tools": [], "run_when": "any_filled" }
}
}
```
For companies: same shape with a `/company/<slug>` resolver and `linkedin_company_profile`.
## PDF extraction (find the document → read it)
```json
{
"input": { "Company": ["Apple"] },
"output": {
"Annual Report URL": { "prompt": "Find the URL of this company's most recent annual report PDF (10-K or equivalent). Only return a real URL found via search.", "contexts": ["Company"], "format": "url" },
"Reported Revenue": { "tool": "pdf", "url": "Annual Report URL", "prompt": "What was total net revenue for the most recent fiscal year? Return the number in USD." }
}
}
```
If the PDF URL is already an input column, skip the first column.
## Product matching across retailers (one JSON column per retailer)
```json
{
"input": { "Product": ["Sony WH-1000XM5"], "Brand": ["Sony"] },
"output": {
"Amazon": {
"prompt": "Check if this exact product is sold on amazon.com. Return match type, URL, list price, sale price, stock status, and review rating.",
"contexts": ["Product", "Brand"],
"format": "json",
"format_details": { "schema": { "type": "object", "properties": { "match_type": { "type": "string", "enum": ["exact", "similar", "not_found"] }, "url": { "type": ["string", "null"] }, "list_price": { "type": ["number", "null"] }, "sale_price": { "type": ["number", "null"] }, "in_stock": { "type": "boolean" }, "review_rating": { "type": ["number", "null"] } }, "required": ["match_type", "url", "list_price", "sale_price", "in_stock", "review_rating"], "additionalProperties": false } }
}
}
}
```
## Image analysis (URL known → tool-only)
```json
{
"input": { "Company": ["Stripe"], "Logo URL": ["https://stripe.com/img/v3/home/social.png"] },
"output": { "Logo Colors": { "tool": "image", "url": "Logo URL", "prompt": "List the dominant colors in this logo as hex codes." } }
}
```
## Transform with code (free)
```json
{
"input": { "First Name": ["Ada"], "Last Name": ["Lovelace"], "Website": ["https://www.example.com/about"] },
"output": {
"Full Name": { "tool": "code", "code": "return `${args.first} ${args.last}`", "args": { "first": "First Name", "last": "Last Name" } },
"Domain": { "tool": "code", "code": "try { return new URL(args.url).hostname.replace(/^www\\./, '') } catch (e) { return null }", "args": { "url": "Website" } }
}
}
```
credits: |
# Credits
Every paid tool accepts two optional fields:
- `dry_run: true` — validate and return the estimate; nothing is created or charged. Response: `{ "dry_run": true, "type": "<run type>", "credit_detail": { "estimate": { "minimum", "maximum", "charged_upfront" }, "credits_used": 0, "credits_remaining", "sufficient_credits" } }`.
- `max_credits` — ceiling. If the estimate's `maximum` exceeds it, the call is refused with `credit_cap_exceeded` and nothing is charged.
Every run carries `credit_detail` on kickoff and on `get_run`. `maximum` is the contract — a run never charges more. `minimum` is 0 when cells can skip (blank inputs, `run_when`, already-filled cells). `charged_upfront` is true for dataset builds and extractions (taken at kickoff, unfound rows refunded), false for enrichments and searches (accrued as work completes).
**Rough pricing** (use `dry_run` for exact numbers): an agent-mode cell = 1 credit; tool-only cells are priced per tool (scrape ≈ 0.05, web_search 0.25, pdf 1, image 0.2, LinkedIn 1–5); `code` = 0. A dataset build = 4 credits per row for the first 250 rows, then 2. `search_agent` = 1 per call; `quick_search` and `scrape` are fractional. Extraction discovery is a one-time charge per site; each extraction run is priced per plan — both shown by `dry_run`.
**Cheapest correct enrichment:** saved `enrichment_id`, `run_when: any_filled` on chained columns, one fetch per source, `code` for transforms, `text`/`number` over `json` where one value suffices.
datasets_vs_enrichment: |
# Dataset build vs enrichment
**A dataset build finds rows. It does not research them.** `build_dataset` returns identifier columns only (e.g. Company + Website). `attributes` you pass are NOT filled in — they are saved as the output columns of an enrichment that runs later, as a separate paid step.
To get attributes filled, either:
- pass `auto_run_enrichment: true` on `build_dataset` (the response also carries `enrichment_run_id`; poll both runs), or
- after the build succeeds, call `enrich` with the `dataset_id` and a config (`enrichment_id`, `prompt` + `attributes`, or `output`).
A build result never contains attribute values. If a user asked for columns and you did not auto-enrich, the next step is `enrich`, not another `build_dataset`.
Limits: `identifiers` ≤ 3, `qualifiers` ≤ 10, `attributes` ≤ 20. `max_items` is capped per account; `dry_run: true` shows the row estimate and price.
`build_dataset_for_enrichment` builds rows shaped for a saved enrichment (identifiers derived from its input columns). `extend_dataset` adds new, deduplicated rows to a finished build.
limits: |
# Limits
| what | limit |
|---|---|
| `enrich` rows with `enrichment_id` | 10,000 per call |
| `enrich` rows with `output` or `prompt` + `attributes` | 1,000 per call |
| `attributes` (enrich and dataset endpoints) | 20 |
| dataset `identifiers` | 3 |
| dataset `qualifiers` | 10 |
| dataset `max_items` | per-account cap; error message states it |
| `get_run_result` `wait` | 0–50 seconds |
| `list_*` `per_page` | 100 (`list_enrichments`: 50) |
| agent tool calls per cell | `max_tool_calls`, default and max 10 |
| JSON schema nesting | 10 levels |
| rate limit | 30 requests / minute per endpoint group (`X-RateLimit-*` headers) |
All list/array limits are also in each tool's schema as `maxItems` / `maximum`.
webhooks: |
# Webhooks
Pass `webhook_url` on any run kickoff (dataset builds: `dataset_webhook_url`) and Riveter POSTs the finished run to it. Events: `run.completed`, `run.stopped`, `run.finished`. Failed deliveries are retried up to 2 times; the endpoint must return 2xx.
Payload (legacy-shaped, shared with the first-generation API):
```json
{
"event": "run.completed",
"run_key": "<uuid>",
"status": "success",
"enrichment_uuid": "<uuid>", "enrichment_name": "My Enrichment",
"credits_used": 2.5,
"completed_at": "2026-01-15T12:00:00Z",
"formatted_data": { "Company": [{ "value": "Apple" }], "Revenue": [{ "value": "383000000000" }] },
"status_details": { "...": "flat status fields" },
"run_status": { "run_key": "...", "status": "success", "...": "..." }
}
```
`run_key` is the run id without the `run_` prefix; `formatted_data` is the same columnar output `get_run_result` returns. Extraction runs use `extraction_plan_run.*` events and add `extraction_plan_uuid`, `run_state`, `records_count`. Monitors deliver only when the monitor's alert rule passes (e.g. `on_diff`).
Use webhooks instead of polling whenever the caller can receive HTTP. If you must poll, `get_run_result` with `wait: 50` is one request per 50 seconds; `get_run` no more than every 10–20 seconds.
errors: |
# Errors
Shape: `{ "error": { "type": "<type>", "message": "<what to fix>" } }`. The `message` names the field and the limit.
| `type` | meaning | do |
|---|---|---|
| `bad_request` / `validation` | malformed or invalid config | read `message`; common causes: `contexts` on a tool-only column, a column referencing a later column, unknown header, too many attributes |
| `not_found` | unknown id | check the prefix (`run_`, `enr_`, `ds_`, `ext_`, `mon_`) and that it belongs to this account |
| `forbidden` | key lacks access | the connection's API key cannot use this resource |
| `duplicate_run_key` | `run_key` already used | pick a new `run_key` or fetch the existing run |
| `insufficient_credits` | balance too low | `credit_detail` shows `credits_remaining`; reduce rows/columns or ask the user to top up |
| `credit_cap_exceeded` | estimate `maximum` > your `max_credits` | raise `max_credits`, or shrink the request; `credit_detail` has the estimate |
| `not_implemented` | unsupported combination | use the alternative named in `message` |
Authentication failures (`401`) use `{ "request_status": "error", "message": "...", "error_type": "unauthorized" }` — the MCP connection's key was revoked; the user must reconnect.
A run that fails after kickoff reports `status: "stopped"` with an `error` on `get_run` — no error envelope, since the kickoff already returned 201.
A `legacy` object in a response (`{ "notice", "successor", "docs" }`) means the call hit a first-generation endpoint (`/run_status`, `/build_dataset`, ...). It still worked; make the next call to the `successor` instead. MCP tools never hit legacy endpoints.
version: 2.1.0
contact:
name: Riveter Support
url: https://riveterhq.com
email: support@riveterhq.com
servers:
- url: https://api.riveterhq.com/v1
description: Production server
security:
- ApiKeyAuth: []
paths:
/enrich:
post:
summary: enrich
description: |
Provide rows in `input`, tell Riveter what columns to add and a webhook to send data to, or get back a run to poll.
There are three ways to tell Riveter what to add. This is in order of preference:
1. Run an existing enrichment (enrichment_id) — preferred.
2. Run with a prompt + attributes — describe the columns in natural language.
3. Run with a full output spec (output) — define each column exactly.
## Input: inline columnar data
`input` is a JSON object in columnar form:
- Keys are column headers.
- Values are arrays of strings. Every array must be the same length.
- Each position across the arrays is one row.
For example, `{ "Company": ["Apple", "Google"], "Website": ["apple.com", "google.com"] }` is two rows:
| Company | Website |
|---------|-----------|
| Apple | apple.com |
| Google | google.com |
## 1. run an existing enrichment (enrichment_id) (preferred)
Pass the id of a saved enrichment (`enr_...`). First build and fine-tune it in the [Riveter UI](https://app.riveterhq.com/enrichments), then run it with new rows. This is the preferred option: the configuration is fixed and tested, so results are the most consistent. Max 10,000 rows per request. Input column headers must match the enrichment's source-data columns.
```bash
curl -X POST "https://api.riveterhq.com/v1/enrich" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"enrichment_id": "enr_YOUR_ENRICHMENT_ID",
"input": {"Company Name": ["Acme Corp", "Tech Solutions Inc"]}
}'
```
## 2. run from a prompt + attributes
Provide a natural-language `prompt` and an `attributes` array of output column names. The AI generates the full column configuration for you. This needs no setup, but the configuration is generated fresh each time, so results can vary between runs. For consistent, repeatable results, save the enrichment once and run it by `enrichment_id` (option 1). Max 1,000 rows per request, max 20 attributes.
```bash
curl -X POST "https://api.riveterhq.com/v1/enrich" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Research each company",
"attributes": ["CEO", "Employee Count", "Industry"],
"input": {"Company": ["Apple", "Google"]},
"webhook_url": "https://your-server.com/webhook"
}'
```
## 3. run from a full output spec
Define each output column exactly: the prompt, contexts, tools, format, and `run_when` (skip rows or chain columns — skipped cells cost 0 credits) per column. Use this when you need precise control over how each column runs. See the `EnrichmentOutputSpec` schema. Max 1,000 rows per request.
```bash
curl -X POST "https://api.riveterhq.com/v1/enrich" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"output": {
"Employee Count": {
"prompt": "Find the number of employees at this company",
"contexts": ["Company"],
"format": "number"
}
},
"input": {"Company": ["Apple", "Google"]}
}'
```
## Use a built dataset as the input
Instead of inline `input`, pass `dataset_id` to enrich the rows of a completed dataset build (`ds_...`). The row source is exactly one of `input` or `dataset_id`, and it combines with any of the three column-config options above.
```bash
curl -X POST "https://api.riveterhq.com/v1/enrich" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"enrichment_id": "enr_YOUR_ENRICHMENT_ID",
"dataset_id": "ds_YOUR_DATASET_ID"
}'
```
## After the kickoff
The response is a [run](#model/run). Poll [GET /runs/{id}](#tag/runs/get/runs/{id}), fetch results with [GET /runs/{id}/result](#tag/runs/get/runs/{id}/result), or just wait for your `webhook_url` to be called.
operationId: enrich
x-mcp-open-world: true
# MCP tool description (replaces `description` above for the MCP server
# only). The docs-site text carries curl samples and headings that cost
# tokens on every tools/list; the deep material lives in `help` topics.
x-mcp-description: |
Add AI-researched columns to rows. Rows come from exactly one of `input` (columnar: `{ "Header": ["row1", "row2"] }`, all arrays equal length) or `dataset_id` (a completed build, `ds_...`).
Column config, exactly one of, in order of preference:
1. `enrichment_id` — run a saved enrichment (`enr_...`, from `list_enrichments`). Fixed, tested config; most consistent. Up to 10,000 rows. Input headers must match the enrichment's input columns (`get_enrichment`).
2. `prompt` + `attributes` — Riveter drafts the column config from a description and up to 20 output column names. No setup; results vary per run. Up to 1,000 rows.
3. `output` — full per-column spec: `prompt`, `contexts`, `tools`, `format`, `run_when`, or a tool-only `tool`. Up to 1,000 rows. Call `help` with `column_config`, `run_when`, `formats`, `tools`, or `examples` before writing one.
Returns a run. Poll `get_run_result` with `wait: 50` (or pass `webhook_url`). `dry_run: true` returns the credit estimate without running.
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const run = await riveter.enrich({
enrichment_id: "enr_YOUR_ENRICHMENT_ID",
input: { "Company Name": ["Acme Corp", "Tech Solutions Inc"] },
});
const result = await riveter.runs.waitForResult(run.id);
console.log(result.output);
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
run = riveter.enrich(
enrichment_id="enr_YOUR_ENRICHMENT_ID",
input={"Company Name": ["Acme Corp", "Tech Solutions Inc"]},
)
result = riveter.runs.wait_for_result(run.id)
print(result.output)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
run = riveter.enrich(
enrichment_id: "enr_YOUR_ENRICHMENT_ID",
input: { "Company Name" => ["Acme Corp", "Tech Solutions Inc"] }
)
result = riveter.runs.wait_for_result(run.id)
puts result.output
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
ctx := context.Background()
run, err := client.Enrich(ctx, riveter.EnrichParams{
EnrichmentID: "enr_YOUR_ENRICHMENT_ID",
Input: map[string][]string{"Company Name": {"Acme Corp", "Tech Solutions Inc"}},
})
result, err := client.Runs.WaitForResult(ctx, run.ID, nil)
fmt.Println(string(result.Output))
tags:
- Enrich
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
enrichment_id:
type: string
description: "Config source: id of a saved enrichment (enr_...)"
output:
$ref: "#/components/schemas/EnrichmentOutputSpec"
description: "Config source: full column spec"
prompt:
type: string
description: "Config source: natural-language instructions (requires attributes)"
attributes:
type: array
items:
type: string
maxItems: 20
description: "Config source: output column names to auto-generate (requires prompt). Max 20."
input:
$ref: "#/components/schemas/EnrichmentInputData"
description: "Row source: inline columnar data"
dataset_id:
type: string
description: "Row source: id of a completed dataset build (ds_...)"
run_key:
type: string
maxLength: 255
pattern: "^[A-Za-z0-9._~-]+$"
description: |
Optional idempotency key, unique per account. Becomes the run id ("run_<run_key>"),
so it is limited to letters, digits, and . _ ~ - characters. A duplicate returns 409.
webhook_url:
type: string
format: uri
description: URL to POST the results to when the run completes
allow_duplicate_input:
type: boolean
default: false
description: With enrichment_id — allow re-running rows already present in the enrichment
dry_run:
$ref: "#/components/schemas/DryRunParam"
max_credits:
$ref: "#/components/schemas/MaxCreditsParam"
examples:
saved_enrichment:
summary: Run a saved enrichment
value:
enrichment_id: "enr_018f5b60-1234-7abc-89ab-0123456789ab"
input:
"Company Name": ["Acme Corp", "Tech Solutions Inc"]
prompt_and_attributes:
summary: Prompt + attributes (auto-generated config)
value:
prompt: "Research each company"
attributes: ["CEO", "Employee Count", "Industry"]
input:
"Company": ["Apple", "Google"]
webhook_url: "https://your-server.com/webhook"
full_output_spec:
summary: Full output specification
value:
output:
"Employee Count":
prompt: "Find the number of employees at this company"
contexts: ["Company"]
format: "number"
input:
"Company": ["Apple", "Google"]
enrich_dataset_rows:
summary: Enrich the rows of a completed dataset build
value:
enrichment_id: "enr_018f5b60-1234-7abc-89ab-0123456789ab"
dataset_id: "ds_018f6a70-1234-7abc-89ab-0123456789ab"
dry_run_estimate:
summary: Price the run without starting it
value:
enrichment_id: "enr_018f5b60-1234-7abc-89ab-0123456789ab"
input:
"Company Name": ["Acme Corp", "Tech Solutions Inc"]
dry_run: true
responses:
"201":
description: Run started — poll /runs/{id} or wait for the webhook
content:
application/json:
schema:
$ref: "#/components/schemas/Run"
"200":
description: dry_run only — the credit estimate; nothing was started
content:
application/json:
schema:
$ref: "#/components/schemas/DryRunResult"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"409":
$ref: "#/components/responses/Conflict"
"422":
$ref: "#/components/responses/UnprocessableEntity"
/runs/{id}:
get:
summary: run status
description: |
This returns the **status and progress** of a run for any run created from enrichments, datasets, or extractions. `status` reports the status of the run, and `progress` gives a completion estimate.
operationId: getRun
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const run = await riveter.runs.get("run_YOUR_RUN_ID");
console.log(run.status, run.progress);
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
run = riveter.runs.get("run_YOUR_RUN_ID")
print(run.status, run.progress)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
run = riveter.runs.get("run_YOUR_RUN_ID")
puts run.status
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
run, err := client.Runs.Get(context.Background(), "run_YOUR_RUN_ID")
fmt.Println(run.Status)
tags:
- Runs
parameters:
- name: id
in: path
required: true
description: The run id (run_...)
schema:
type: string
responses:
"200":
description: The run
content:
application/json:
schema:
$ref: "#/components/schemas/Run"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
/runs/{id}/result:
get:
summary: run result
description: |
The run plus `output`. `output` is `null` until the run reaches a terminal state..
Pass `?wait=N` (max 50) to long-poll: the request holds until the run finishes or the budget elapses, whichever comes first.
## Output shape by run type
- **enrichment** — an object mapping column headers to arrays of cell objects: `{ "Company": [{"value": "Apple"}], "CEO": [{"value": "Tim Cook"}] }`
- **dataset_build** — an object mapping column headers to arrays of cell objects (same columnar shape as enrichment)
- **extraction** — the extracted records as an array of JSON objects (matching your `output_record_json_schema`)
- **quick_search** — the search result object `{ "results": [{ "title", "link", "snippet" }, ...], "knowledge_graph"? }`, the same data the synchronous `POST /quick_search` response already carried. (Runs started on the legacy async `/web_search` endpoint share this run type but return the columnar enrichment shape with a `search_results` column.)
- **search_agent** — the answer object `{ "result": <string or object> }`; `result` matches the request's `output_schema` when one was given, otherwise it's free text.
operationId: getRunResult
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
// One 30s long-poll:
const result = await riveter.runs.result("run_YOUR_RUN_ID", { wait: 30 });
// Or keep polling until the run finishes (default budget 10 min):
const finished = await riveter.runs.waitForResult("run_YOUR_RUN_ID");
console.log(finished.output);
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
# One 30s long-poll:
result = riveter.runs.result("run_YOUR_RUN_ID", wait=30)
# Or keep polling until the run finishes (default budget 10 min):
finished = riveter.runs.wait_for_result("run_YOUR_RUN_ID")
print(finished.output)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
# One 30s long-poll:
result = riveter.runs.result("run_YOUR_RUN_ID", wait: 30)
# Or keep polling until the run finishes (default budget 10 min):
finished = riveter.runs.wait_for_result("run_YOUR_RUN_ID")
puts finished.output
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
ctx := context.Background()
// One 30s long-poll:
result, err := client.Runs.Result(ctx, "run_YOUR_RUN_ID", &riveter.ResultOptions{Wait: 30})
// Or keep polling until the run finishes (default budget 10 min):
finished, err := client.Runs.WaitForResult(ctx, "run_YOUR_RUN_ID", nil)
fmt.Println(string(finished.Output))
tags:
- Runs
parameters:
- name: id
in: path
required: true
description: The run id (run_...)
schema:
type: string
- name: wait
in: query
required: false
description: Long-poll budget in seconds (0–50, default 0)
schema:
type: integer
minimum: 0
maximum: 50
default: 0
responses:
"200":
description: The run with output (null while still running)
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/Run"
- type: object
properties:
output:
description: The run's results (null until ready) — shape depends on run type, see the endpoint description
type: [object, array, "null"]
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"422":
$ref: "#/components/responses/UnprocessableEntity"
/runs/{id}/stop:
post:
summary: stop run
description: |
Stop a run early. Works for every run type. Already-finished runs are left untouched; the response is the run either way.
operationId: stopRun
x-mcp-destructive: true
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const run = await riveter.runs.stop("run_YOUR_RUN_ID");
console.log(run.status); // "stopped"
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
run = riveter.runs.stop("run_YOUR_RUN_ID")
print(run.status) # "stopped"
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
run = riveter.runs.stop("run_YOUR_RUN_ID")
puts run.status # "stopped"
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
run, err := client.Runs.Stop(context.Background(), "run_YOUR_RUN_ID")
fmt.Println(run.Status) // "stopped"
tags:
- Runs
parameters:
- name: id
in: path
required: true
description: The run id (run_...)
schema:
type: string
responses:
"200":
description: The run after the stop
content:
application/json:
schema:
$ref: "#/components/schemas/Run"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"422":
$ref: "#/components/responses/UnprocessableEntity"
/runs:
get:
summary: list runs
description: |
List the account's runs, newest first. Every async operation shows up here — enrichment runs, dataset builds, extractions, and quick searches.
Filter by `type` (comma-separated), `status`, `enrichment_id`, `monitor_id`, and `created_after` / `created_before` (ISO 8601). Paginate with `page` / `per_page`.
operationId: listRuns
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const page = await riveter.runs.list({ status: "success" });
for await (const run of page) { // auto-pages through every result
console.log(run.id, run.type);
}
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
page = riveter.runs.list(status="success")
for run in page.auto_paging_iter(): # pages through every result
print(run.id, run.type)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
page = riveter.runs.list(status: "success")
page.auto_paging_each do |run| # pages through every result
puts "#{run.id} #{run.type}"
end
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
ctx := context.Background()
page, err := client.Runs.List(ctx, &riveter.ListRunsParams{Status: "success"})
for {
for _, run := range page.Runs {
fmt.Println(run.ID, run.Type)
}
if !page.HasNextPage() {
break
}
page, err = page.NextPage(ctx)
}
tags:
- Runs
parameters:
- name: type
in: query
required: false
description: "Comma-separated run types: enrichment, dataset_build, extraction, scrape, quick_search, search_agent"
schema:
type: string
- name: status
in: query
required: false
description: Filter by run status
schema:
type: string
enum: [pending, enqueued, processing, success, stopped]
- name: enrichment_id
in: query
required: false
description: Only runs of this enrichment (enr_...)
schema:
type: string
- name: monitor_id
in: query
required: false
description: Only runs of this monitor (mon_...)
schema:
type: string
- name: created_after
in: query
required: false
schema:
type: string
format: date-time
- name: created_before
in: query
required: false
schema:
type: string
format: date-time
- name: page
in: query
required: false
schema:
type: integer
default: 1
- name: per_page
in: query
required: false
schema:
type: integer
default: 25
maximum: 100
responses:
"200":
description: Runs listed
content:
application/json:
schema:
type: object
properties:
runs:
type: array
items:
$ref: "#/components/schemas/RunListItem"
pagination:
$ref: "#/components/schemas/Pagination"
required:
- runs
- pagination
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
/runs/summary:
get:
summary: list runs (summary)
description: |
All-time run counts by status — a snapshot of the account's run queue. For the runs themselves use [GET /runs](#tag/runs/get/runs)`?status=...`.
operationId: runsSummary
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const summary = await riveter.runs.summary();
console.log(summary.counts);
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
summary = riveter.runs.summary()
print(summary.counts)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
summary = riveter.runs.summary
puts summary.counts
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
summary, err := client.Runs.Summary(context.Background())
fmt.Printf("%+v\n", summary.Counts)
tags:
- Runs
responses:
"200":
description: Run counts by status
content:
application/json:
schema:
type: object
properties:
counts:
type: object
properties:
pending:
type: integer
enqueued:
type: integer
processing:
type: integer
success:
type: integer
stopped:
type: integer
example:
counts:
pending: 0
enqueued: 1
processing: 2
success: 40
stopped: 3
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
/enrichments/{id}:
get:
summary: read enrichment
description: |
The enrichment's structure: its input (source-data) columns and full output column configuration — the same shape you would send to [POST /enrich](#tag/enrich/post/enrich) as `output`.
operationId: getEnrichment
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const enrichment = await riveter.enrichments.get("enr_YOUR_ENRICHMENT_ID");
console.log(enrichment.input, enrichment.output);
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
enrichment = riveter.enrichments.get("enr_YOUR_ENRICHMENT_ID")
print(enrichment.input, enrichment.output)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
enrichment = riveter.enrichments.get("enr_YOUR_ENRICHMENT_ID")
puts enrichment.input.inspect
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
enrichment, err := client.Enrichments.Get(context.Background(), "enr_YOUR_ENRICHMENT_ID")
fmt.Println(enrichment.Input)
tags:
- Enrich
parameters:
- name: id
in: path
required: true
description: The enrichment id (enr_...)
schema:
type: string
responses:
"200":
description: Enrichment structure
content:
application/json:
schema:
type: object
properties:
id:
type: string
name:
type: string
status:
type: string
app_url:
type: string
format: uri
input:
type: object
description: Source-data (input) columns
output:
$ref: "#/components/schemas/EnrichmentOutputSpec"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
patch:
summary: update enrichment
description: |
Add, update, rename, or delete output columns, or reorder columns. Send the column changes keyed by column header inside `output` (recommended); a `column_order` array reorders columns.
Existing columns can be partially updated; new column names must include a full configuration; set `"delete": true` on a column to remove it. Columns use the same fields as the `output` spec on [POST /enrich](#tag/enrich/post/enrich).
operationId: updateEnrichment
x-mcp-destructive: true
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const changes = await riveter.enrichments.update("enr_YOUR_ENRICHMENT_ID", {
output: {
CEO: { prompt: "Find the CEO's full name", contexts: ["Company"] },
},
});
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
changes = riveter.enrichments.update(
"enr_YOUR_ENRICHMENT_ID",
output={"CEO": {"prompt": "Find the CEO's full name", "contexts": ["Company"]}},
)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
changes = riveter.enrichments.update(
"enr_YOUR_ENRICHMENT_ID",
output: { "CEO" => { prompt: "Find the CEO's full name", contexts: ["Company"] } }
)
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
changes, err := client.Enrichments.Update(context.Background(), "enr_YOUR_ENRICHMENT_ID",
riveter.UpdateEnrichmentParams{
Output: map[string]riveter.OutputColumnConfig{
"CEO": {Prompt: "Find the CEO's full name", Contexts: []string{"Company"}},
},
})
tags:
- Enrich
parameters:
- name: id
in: path
required: true
description: The enrichment id (enr_...)
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
output:
$ref: "#/components/schemas/EnrichmentOutputSpec"
description: Column changes keyed by column header
column_order:
type: array
items:
type: string
description: Optional full column ordering (column headers)
examples:
add_a_column:
summary: Add a column
value:
output:
"CEO":
prompt: "Find the company's CEO"
contexts: ["Company Name"]
format: "text"
delete_a_column:
summary: Delete a column
value:
output:
"Old Column":
delete: true
responses:
"200":
description: Enrichment updated — response lists the applied changes
content:
application/json:
schema:
type: object
properties:
id:
type: string
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"422":
$ref: "#/components/responses/UnprocessableEntity"
/enrichments/{id}/datasets:
post:
summary: enrichment → dataset
description: |
Build a dataset **shaped for this enrichment**: identifiers are derived from the enrichment's source-data columns automatically, so generated rows land as valid input rows. The build finds rows only; the enrichment's output columns are not researched until the enrichment runs.
Optionally set `auto_run_enrichment: true` to run the enrichment on the rows as soon as the build completes (a second paid run; the kickoff response then carries `enrichment_run_id`).
Returns the dataset-build run — poll it via [GET /runs/{id}](#tag/runs/get/runs/{id}).
operationId: buildDatasetForEnrichment
x-mcp-open-world: true
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const run = await riveter.enrichments.buildDataset("enr_YOUR_ENRICHMENT_ID", {
prompt: "US fintech startups",
max_items: 100,
auto_run_enrichment: true,
});
const result = await riveter.runs.waitForResult(run.id);
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
run = riveter.enrichments.build_dataset(
"enr_YOUR_ENRICHMENT_ID",
prompt="US fintech startups",
max_items=100,
auto_run_enrichment=True,
)
result = riveter.runs.wait_for_result(run.id)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
run = riveter.enrichments.build_dataset(
"enr_YOUR_ENRICHMENT_ID",
prompt: "US fintech startups",
max_items: 100,
auto_run_enrichment: true
)
result = riveter.runs.wait_for_result(run.id)
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
ctx := context.Background()
run, err := client.Enrichments.BuildDataset(ctx, "enr_YOUR_ENRICHMENT_ID",
riveter.BuildDatasetForEnrichmentParams{
Prompt: "US fintech startups",
MaxItems: 100,
AutoRunEnrichment: true,
})
result, err := client.Runs.WaitForResult(ctx, run.ID, nil)
tags:
- Enrich
parameters:
- name: id
in: path
required: true
description: The enrichment id (enr_...)
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
prompt:
type: string
description: What rows to generate (e.g. "US-based fintech startups")
qualifiers:
type: array
items:
type: string
maxItems: 10
description: Optional constraints each row must satisfy (e.g. "B2B", "founded after 2015"). Max 10.
max_items:
type: integer
description: Max rows to generate (capped by your plan)
dataset_webhook_url:
type: string
format: uri
description: URL to POST the dataset results to when the build completes
auto_run_enrichment:
type: boolean
default: false
description: Run the enrichment automatically when the build completes
auto_run_enrichment_webhook_url:
type: string
format: uri
description: Webhook for the auto-run enrichment results
dry_run:
$ref: "#/components/schemas/DryRunParam"
max_credits:
$ref: "#/components/schemas/MaxCreditsParam"
required:
- prompt
examples:
build_and_auto_run:
summary: Build rows and auto-run the enrichment
value:
prompt: "US-based fintech startups"
qualifiers: ["B2B", "founded after 2015"]
max_items: 100
auto_run_enrichment: true
responses:
"201":
description: Dataset build started — the run plus dataset_id / enrichment_id / max_items (and enrichment_run_id when auto-running)
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/Run"
- type: object
properties:
dataset_id:
type: string
enrichment_id:
type: string
max_items:
type: integer
enrichment_run_id:
type: string
description: Present when auto_run_enrichment is true
"200":
description: dry_run only — the credit estimate (list-gen price plus the enrichment's upper bound when auto_run_enrichment is true); nothing was started
content:
application/json:
schema:
$ref: "#/components/schemas/DryRunResult"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"422":
$ref: "#/components/responses/UnprocessableEntity"
/enrichments:
post:
summary: create enrichment
description: |
Create an enrichment (no run) from a **completed dataset build**: the dataset's rows become the enrichment's input rows, and its attributes become output columns. Configure further in the UI or via [PATCH /enrichments/{id}](#tag/enrich/patch/enrichments/{id}), then run with [POST /enrich](#tag/enrich/post/enrich).
operationId: createEnrichment
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const created = await riveter.enrichments.create({
dataset_id: "ds_YOUR_DATASET_ID",
});
console.log(created.id); // enr_...
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
created = riveter.enrichments.create(dataset_id="ds_YOUR_DATASET_ID")
print(created.id) # enr_...
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
created = riveter.enrichments.create(dataset_id: "ds_YOUR_DATASET_ID")
puts created.id # enr_...
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
created, err := client.Enrichments.Create(context.Background(), "ds_YOUR_DATASET_ID")
fmt.Println(created.ID) // enr_...
tags:
- Enrich
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
dataset_id:
type: string
description: Id of a completed dataset build (ds_...)
required:
- dataset_id
responses:
"201":
description: Enrichment created
content:
application/json:
schema:
type: object
properties:
id:
type: string
description: The new enrichment's id (enr_...)
name:
type: string
app_url:
type: string
format: uri
dataset_id:
type: string
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
get:
summary: list enrichments
description: |
List the account's enrichments, most recently updated first. Each item is compact — id, name, status, timestamps, and column names/count — so the list stays small even for accounts with thousands of enrichments. Column configuration (prompts, tools, formats) is on [GET /enrichments/{id}](#tag/enrich/get/enrichments/{id}).
Filter by `status` (comma-separated) and `name` (case-insensitive substring). Paginate with `page` / `per_page` (max 50). Column names are capped at 40 per input/output list; when cut, `column_names_truncated` is `true` and `column_count` still reports the total.
operationId: listEnrichments
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const page = await riveter.enrichments.list({ name: "contact" });
for await (const enrichment of page) {
console.log(enrichment.id, enrichment.name, enrichment.input_columns);
}
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
page = riveter.enrichments.list(name="contact")
for enrichment in page.auto_paging_iter():
print(enrichment.id, enrichment.name, enrichment.input_columns)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
riveter.enrichments.list(name: "contact").auto_paging_each do |enrichment|
puts "#{enrichment.id} #{enrichment.name} #{enrichment.input_columns}"
end
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
page, err := client.Enrichments.List(context.Background(), &riveter.ListEnrichmentsParams{Name: "contact"})
for _, enrichment := range page.Enrichments {
fmt.Println(enrichment.ID, enrichment.Name, enrichment.InputColumns)
}
tags:
- Enrich
parameters:
- name: status
in: query
required: false
description: "Comma-separated: pending, enqueued, processing, success, stopped"
schema:
type: string
- name: name
in: query
required: false
description: Case-insensitive substring match on the enrichment name
schema:
type: string
- name: page
in: query
required: false
schema:
type: integer
default: 1
- name: per_page
in: query
required: false
schema:
type: integer
default: 25
maximum: 50
responses:
"200":
description: Enrichments listed
content:
application/json:
schema:
type: object
properties:
enrichments:
type: array
items:
type: object
properties:
id:
type: string
description: Enrichment id (enr_...)
name:
type: string
status:
type: string
enum:
[pending, enqueued, processing, success, stopped]
app_url:
type: string
format: uri
column_count:
type: integer
description: Total number of columns (input + output)
input_columns:
type: array
description: Source-data column headers — the keys `input` must use when running this enrichment (first 40)
items:
type: string
output_columns:
type: array
description: Output column headers (first 40)
items:
type: string
column_names_truncated:
type: boolean
description: Present and true only when a column-name list was cut at 40 entries
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
pagination:
$ref: "#/components/schemas/Pagination"
example:
enrichments:
- id: enr_0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b
name: Deal accounting contact finder
status: success
app_url: https://app.riveterhq.com/enrichments/enr_0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b
column_count: 3
input_columns: [Company]
output_columns: [CFO name, CFO email]
created_at: "2026-08-01T12:00:00Z"
updated_at: "2026-09-01T09:30:00Z"
pagination:
page: 1
per_page: 25
total_count: 1
total_pages: 1
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
/enrichments/summary:
get:
summary: list enrichments (summary)
description: |
All-time enrichment counts by status — how many of the account's enrichments are pending, running, finished, or stopped. For the enrichments themselves use [GET /enrichments](#tag/enrich/get/enrichments)`?status=...`.
operationId: enrichmentsSummary
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const summary = await riveter.enrichments.summary();
console.log(summary.counts);
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
summary = riveter.enrichments.summary()
print(summary.counts)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
summary = riveter.enrichments.summary
puts summary.counts
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
summary, err := client.Enrichments.Summary(context.Background())
fmt.Printf("%+v\n", summary.Counts)
tags:
- Enrich
responses:
"200":
description: Enrichment counts by status
content:
application/json:
schema:
type: object
properties:
counts:
type: object
properties:
pending:
type: integer
enqueued:
type: integer
processing:
type: integer
success:
type: integer
stopped:
type: integer
example:
counts:
pending: 12
enqueued: 0
processing: 1
success: 340
stopped: 6
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
/datasets:
post:
summary: build dataset
description: |
Build a dataset from a natural-language prompt, a structured spec, or both. Riveter finds the rows for you.
- **Prompt only** — e.g. "top 50 US SaaS companies with their websites". The builder analyzes the prompt into identifiers/qualifiers/attributes automatically.
- **Structured spec** — `identifiers` (what each row is, e.g. "Company name") and `qualifiers` (constraints rows must satisfy). Optional `attributes` are saved as the enrichment columns described above.
- **Both** — the prompt is combined with the spec.
**Note**: The result contains the identifier columns only (e.g. company name + website). `attributes` are not immediately filled in by the build — they become the output columns of an enrichment that can run later, as a separate step. To get attributes filled: pass `auto_run_enrichment: true`, or call [POST /enrich](#tag/enrich/post/enrich) with `dataset_id` once the build completes. This is a second paid run: `dry_run` shows the combined estimate.
Returns the dataset-build run; poll [GET /runs/{id}](#tag/runs/get/runs/{id}) and fetch rows with [GET /runs/{id}/result](#tag/runs/get/runs/{id}/result), or pass `dataset_webhook_url`.
operationId: buildDataset
x-mcp-open-world: true
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const run = await riveter.datasets.build({
prompt: "Top 100 US fintech startups",
max_items: 100,
});
const result = await riveter.runs.waitForResult(run.id);
console.log(result.output);
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
run = riveter.datasets.build(
prompt="Top 100 US fintech startups",
max_items=100,
)
result = riveter.runs.wait_for_result(run.id)
print(result.output)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
run = riveter.datasets.build(
prompt: "Top 100 US fintech startups",
max_items: 100
)
result = riveter.runs.wait_for_result(run.id)
puts result.output
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
ctx := context.Background()
run, err := client.Datasets.Build(ctx, riveter.BuildDatasetParams{
Prompt: "Top 100 US fintech startups",
MaxItems: 100,
})
result, err := client.Runs.WaitForResult(ctx, run.ID, nil)
fmt.Println(string(result.Output))
tags:
- Datasets
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
prompt:
type: string
description: Natural-language description of the dataset (required unless identifiers are given)
identifiers:
type: array
items:
type: string
maxItems: 3
description: What each row is (e.g. ["Company name", "Website"]). Max 3.
qualifiers:
type: array
items:
type: string
maxItems: 10
description: Constraints each row must satisfy. Max 10.
attributes:
type: array
items:
type: string
maxItems: 20
description: |
Output columns for a LATER enrichment (e.g. ["CEO", "Employee count"]). Max 20. Not filled in by the
build — the dataset result has identifier columns only. Filled when auto_run_enrichment is true or
when you POST /enrich with this dataset_id.
max_items:
type: integer
description: Max rows to generate (capped by your plan)
dataset_webhook_url:
type: string
format: uri
description: URL to POST the dataset results to when the build completes
auto_run_enrichment:
type: boolean
default: false
description: |
After the build completes, create an enrichment (one output column per attribute) and run it on the
rows. A second paid run; the kickoff response carries its id as enrichment_run_id.
auto_run_enrichment_webhook_url:
type: string
format: uri
description: Webhook for the auto-run enrichment results
dry_run:
$ref: "#/components/schemas/DryRunParam"
max_credits:
$ref: "#/components/schemas/MaxCreditsParam"
examples:
prompt_with_spec:
summary: Prompt with identifiers and qualifiers
value:
prompt: "Top 50 US SaaS companies with their websites"
identifiers: ["Company name", "Website"]
qualifiers: ["US-based", "SaaS"]
max_items: 50
capped_build:
summary: Refuse the build if it could cost more than 200 credits
value:
prompt: "Top 50 US SaaS companies with their websites"
max_items: 50
max_credits: 200
structured_spec:
summary: Structured spec
value:
identifiers: ["Company name", "Website"]
qualifiers: ["US-based", "SaaS"]
attributes: ["CEO", "Employee count"]
max_items: 50
build_and_enrich:
summary: Build and auto-enrich in one step
value:
prompt: "Top 50 US SaaS companies"
attributes: ["CEO", "Revenue"]
max_items: 50
auto_run_enrichment: true
auto_run_enrichment_webhook_url: "https://your-server.com/webhook"
responses:
"201":
description: Dataset build started — the run plus dataset_id / max_items (and enrichment_run_id when auto-running)
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/Run"
- type: object
properties:
dataset_id:
type: string
max_items:
type: integer
enrichment_run_id:
type: string
description: Present when auto_run_enrichment is true
"200":
description: dry_run only — the credit estimate (list-gen price on max_items, plus attributes × max_items when auto_run_enrichment is true); nothing was started
content:
application/json:
schema:
$ref: "#/components/schemas/DryRunResult"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"422":
$ref: "#/components/responses/UnprocessableEntity"
/datasets/{id}/extend:
post:
summary: extend dataset
description: |
Generate **new rows** for an existing completed dataset build. The new rows are deduplicated against the source build's rows. Identifiers and attributes are inherited from the source and cannot be overridden; `qualifiers` and `max_items` may be replaced, and an optional `prompt` adds a new instruction.
Like [POST /datasets](#tag/datasets/post/datasets), this finds rows only (identifier columns). Inherited attributes are not researched; they are filled by `auto_run_enrichment: true` or a later [POST /enrich](#tag/enrich/post/enrich) with the new `dataset_id`.
Returns a fresh dataset-build run (the source build is untouched).
operationId: extendDataset
x-mcp-open-world: true
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const run = await riveter.datasets.extend("ds_YOUR_DATASET_ID", {
max_items: 50,
});
const result = await riveter.runs.waitForResult(run.id);
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
run = riveter.datasets.extend("ds_YOUR_DATASET_ID", max_items=50)
result = riveter.runs.wait_for_result(run.id)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
run = riveter.datasets.extend_dataset("ds_YOUR_DATASET_ID", max_items: 50)
result = riveter.runs.wait_for_result(run.id)
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
ctx := context.Background()
run, err := client.Datasets.Extend(ctx, "ds_YOUR_DATASET_ID",
riveter.ExtendDatasetParams{MaxItems: 50})
result, err := client.Runs.WaitForResult(ctx, run.ID, nil)
tags:
- Datasets
parameters:
- name: id
in: path
required: true
description: Id of the source dataset build (ds_...)
schema:
type: string
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
prompt:
type: string
description: Optional new instruction (e.g. "more rows like these but in Europe")
qualifiers:
type: array
items:
type: string
maxItems: 10
description: Optional replacement qualifiers (defaults to the source's). Max 10.
max_items:
type: integer
description: Max new rows (defaults to the source's max_items)
dataset_webhook_url:
type: string
format: uri
auto_run_enrichment:
type: boolean
default: false
auto_run_enrichment_webhook_url:
type: string
format: uri
dry_run:
$ref: "#/components/schemas/DryRunParam"
max_credits:
$ref: "#/components/schemas/MaxCreditsParam"
responses:
"201":
description: Extension build started — the run plus dataset_id / source_dataset_id / max_items
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/Run"
- type: object
properties:
dataset_id:
type: string
description: The new build's dataset id
source_dataset_id:
type: string
max_items:
type: integer
"200":
description: dry_run only — the credit estimate; nothing was started
content:
application/json:
schema:
$ref: "#/components/schemas/DryRunResult"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"422":
$ref: "#/components/responses/UnprocessableEntity"
/configured_datasets/{id}/build:
post:
summary: build configured dataset
description: |
Run a **configured dataset** — a reusable, pre-configured dataset template (id `cds_...`) set up for your account. The spec, prompt template, row cap, and per-run credit cost are all defined on the template; you only supply `parameters` to fill in its `{{ placeholder }}` values.
Returns the dataset-build run; poll [GET /runs/{id}](#tag/runs/get/runs/{id}).
operationId: buildConfiguredDataset
x-mcp-open-world: true
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const run = await riveter.configuredDatasets.build("cds_YOUR_CONFIGURED_DATASET_ID", {
parameters: { City: "Denver" },
});
const result = await riveter.runs.waitForResult(run.id);
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
run = riveter.configured_datasets.build(
"cds_YOUR_CONFIGURED_DATASET_ID",
parameters={"City": "Denver"},
)
result = riveter.runs.wait_for_result(run.id)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
run = riveter.configured_datasets.build(
"cds_YOUR_CONFIGURED_DATASET_ID",
parameters: { "City" => "Denver" }
)
result = riveter.runs.wait_for_result(run.id)
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
ctx := context.Background()
run, err := client.ConfiguredDatasets.Build(ctx, "cds_YOUR_CONFIGURED_DATASET_ID",
riveter.BuildConfiguredDatasetParams{Parameters: map[string]string{"City": "Denver"}})
result, err := client.Runs.WaitForResult(ctx, run.ID, nil)
tags:
- Datasets
parameters:
- name: id
in: path
required: true
description: Configured dataset id (cds_...)
schema:
type: string
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
parameters:
type: object
description: Values for the template's placeholders (keys defined by the template)
tier:
type: string
description: Optional pricing/depth tier when the template defines tiers
dataset_webhook_url:
type: string
format: uri
description: URL to POST the dataset results to when the build completes
dry_run:
$ref: "#/components/schemas/DryRunParam"
max_credits:
$ref: "#/components/schemas/MaxCreditsParam"
examples:
with_parameters:
summary: Fill in template parameters
value:
parameters:
practice_type: "dentists"
state: "ohio"
responses:
"201":
description: Build started — the run plus dataset_id / configured_dataset_id / max_items / tier / credits_charged
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/Run"
- type: object
properties:
dataset_id:
type: string
configured_dataset_id:
type: string
max_items:
type: integer
tier:
type: [string, "null"]
credits_charged:
type: number
"200":
description: dry_run only — the tier's flat price; nothing was started
content:
application/json:
schema:
$ref: "#/components/schemas/DryRunResult"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"422":
$ref: "#/components/responses/UnprocessableEntity"
/extractions/{id}/runs:
post:
summary: run extraction
description: |
Execute a `ready` extraction. Returns a run — poll [GET /runs/{id}](#tag/runs/get/runs/{id}) and fetch the extracted records with [GET /runs/{id}/result](#tag/runs/get/runs/{id}/result) (the records come back as an array of JSON objects matching your schema), or pass a `webhook_url`.
`variables` fills any `{{ placeholder }}` values the plan defines (e.g. a search term or location). Each run charges run credits (`credits_charged`).
operationId: runExtraction
x-mcp-open-world: true
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const run = await riveter.extractions.run("ext_YOUR_EXTRACTION_ID", {
variables: { location: "Denver" },
});
const result = await riveter.runs.waitForResult(run.id);
console.log(result.output);
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
run = riveter.extractions.run(
"ext_YOUR_EXTRACTION_ID",
variables={"location": "Denver"},
)
result = riveter.runs.wait_for_result(run.id)
print(result.output)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
run = riveter.extractions.run(
"ext_YOUR_EXTRACTION_ID",
variables: { "location" => "Denver" }
)
result = riveter.runs.wait_for_result(run.id)
puts result.output
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
ctx := context.Background()
run, err := client.Extractions.Run(ctx, "ext_YOUR_EXTRACTION_ID",
riveter.RunExtractionParams{Variables: map[string]string{"location": "Denver"}})
result, err := client.Runs.WaitForResult(ctx, run.ID, nil)
fmt.Println(string(result.Output))
tags:
- Extractions
parameters:
- name: id
in: path
required: true
description: The extraction id (ext_...)
schema:
type: string
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
variables:
type: object
description: Values for the plan's placeholders
webhook_url:
type: string
format: uri
description: URL to POST the records to when the run completes
dry_run:
$ref: "#/components/schemas/DryRunParam"
max_credits:
$ref: "#/components/schemas/MaxCreditsParam"
responses:
"201":
description: Extraction run started — the run plus merged variables / credits_charged (and a validation_warning when the plan's last discovery validation did not pass)
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/Run"
- type: object
properties:
variables:
type: object
credits_charged:
type: number
validation_warning:
type: string
description: Present when the last discovery validation did not pass
"200":
description: dry_run only — the flat per-run price (also shown as run_credits_required on GET /extractions/{id}); nothing was started
content:
application/json:
schema:
$ref: "#/components/schemas/DryRunResult"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"422":
$ref: "#/components/responses/UnprocessableEntity"
/extractions:
post:
summary: create extraction
description: |
Create an **extraction** — a reusable recipe for scraping structured records from a website — and start its agent discovery. Discovery explores the site, builds the scrape/extract plan, and validates it against your schema.
Poll [GET /extractions/{id}](#tag/extractions/get/extractions/{id}) until `status` is `ready`, then execute it with [POST /extractions/{id}/runs](#tag/extractions/post/extractions/{id}/runs). The extraction (`ext_...`) and its runs (`run_...`) are different resources.
Creating an extraction charges discovery credits (returned as `credits_charged`). To see the price first, send `dry_run: true`.
operationId: createExtraction
x-mcp-open-world: true
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const extraction = await riveter.extractions.create({
starting_url: "https://example.com/directory",
goal_description: "Extract every listed company",
output_record_json_schema: {
type: "object",
properties: { name: { type: "string" }, website: { type: "string" } },
},
});
console.log(extraction.id); // ext_...
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
extraction = riveter.extractions.create(
starting_url="https://example.com/directory",
goal_description="Extract every listed company",
output_record_json_schema={
"type": "object",
"properties": {"name": {"type": "string"}, "website": {"type": "string"}},
},
)
print(extraction.id) # ext_...
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
extraction = riveter.extractions.create(
starting_url: "https://example.com/directory",
goal_description: "Extract every listed company",
output_record_json_schema: {
"type" => "object",
"properties" => { "name" => { "type" => "string" }, "website" => { "type" => "string" } }
}
)
puts extraction.id # ext_...
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
extraction, err := client.Extractions.Create(context.Background(),
riveter.CreateExtractionParams{
StartingURL: "https://example.com/directory",
GoalDescription: "Extract every listed company",
OutputRecordJSONSchema: map[string]any{
"type": "object",
"properties": map[string]any{
"name": map[string]any{"type": "string"},
"website": map[string]any{"type": "string"},
},
},
})
fmt.Println(extraction.ID) // ext_...
tags:
- Extractions
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
starting_url:
type: string
format: uri
description: Where the agent starts exploring
goal_description:
type: string
description: What records to extract, in plain language
output_record_json_schema:
type: object
description: JSON schema of one output record
name:
type: string
description: Optional display name
required_keys:
type: array
items:
type: string
description: Record keys that must be non-empty for a record to count
dry_run:
$ref: "#/components/schemas/DryRunParam"
max_credits:
$ref: "#/components/schemas/MaxCreditsParam"
required:
- starting_url
- goal_description
- output_record_json_schema
examples:
create:
summary: Create an extraction
value:
starting_url: "https://example.com/products"
goal_description: "Extract every product with its name and price"
output_record_json_schema:
type: object
properties:
name:
type: string
price:
type: string
required_keys: ["name"]
responses:
"201":
description: Extraction created, discovery started
content:
application/json:
schema:
type: object
properties:
id:
type: string
description: Extraction id (ext_...)
name:
type: string
status:
type: string
enum: [discovering]
app_url:
type: string
format: uri
credits_charged:
type: number
credit_detail:
$ref: "#/components/schemas/CreditDetail"
required_keys:
type: array
items:
type: string
"200":
description: dry_run only — the discovery price (type extraction_discovery); nothing was created or charged
content:
application/json:
schema:
$ref: "#/components/schemas/DryRunResult"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"422":
$ref: "#/components/responses/UnprocessableEntity"
/extractions/{id}:
get:
summary: get extraction
description: |
The extraction's status and definition. `status` is `discovering` while the agent builds the plan, then `ready` (or `discovery_failed`). Once `ready`, execute with [POST /extractions/{id}/runs](#tag/extractions/post/extractions/{id}/runs).
operationId: getExtraction
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const extraction = await riveter.extractions.get("ext_YOUR_EXTRACTION_ID");
console.log(extraction.status); // "ready" once discovery finishes
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
extraction = riveter.extractions.get("ext_YOUR_EXTRACTION_ID")
print(extraction.status) # "ready" once discovery finishes
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
extraction = riveter.extractions.get("ext_YOUR_EXTRACTION_ID")
puts extraction.status # "ready" once discovery finishes
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
extraction, err := client.Extractions.Get(context.Background(), "ext_YOUR_EXTRACTION_ID")
fmt.Println(extraction.Status) // "ready" once discovery finishes
tags:
- Extractions
parameters:
- name: id
in: path
required: true
description: The extraction id (ext_...)
schema:
type: string
responses:
"200":
description: Extraction status and definition
content:
application/json:
schema:
$ref: "#/components/schemas/Extraction"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
# (No PATCH/DELETE — manage extractions from the app UI.)
/monitors:
post:
summary: create monitor
description: |
Create a **monitor**: a schedule that re-runs an enrichment daily, weekly, or monthly and can POST results (or only changes) to a webhook.
operationId: createMonitor
x-mcp-open-world: true
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const monitor = await riveter.monitors.create({
enrichment_id: "enr_YOUR_ENRICHMENT_ID",
cadence: "daily",
minute: 0,
hour: 9,
timezone: "America/New_York",
webhook_url: "https://your-server.com/webhook",
});
console.log(monitor.id, monitor.next_run_at);
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
monitor = riveter.monitors.create(
enrichment_id="enr_YOUR_ENRICHMENT_ID",
cadence="daily",
minute=0,
hour=9,
timezone="America/New_York",
webhook_url="https://your-server.com/webhook",
)
print(monitor.id, monitor.next_run_at)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
monitor = riveter.monitors.create(
enrichment_id: "enr_YOUR_ENRICHMENT_ID",
cadence: "daily",
minute: 0,
hour: 9,
timezone: "America/New_York",
webhook_url: "https://your-server.com/webhook"
)
puts "#{monitor.id} #{monitor.next_run_at}"
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
monitor, err := client.Monitors.Create(context.Background(),
riveter.CreateMonitorParams{
EnrichmentID: "enr_YOUR_ENRICHMENT_ID",
Cadence: "daily",
Minute: 0,
Hour: 9,
Timezone: "America/New_York",
WebhookURL: "https://your-server.com/webhook",
})
fmt.Println(monitor.ID, monitor.NextRunAt)
tags:
- Monitors
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
enrichment_id:
type: string
description: Id of the enrichment to monitor (enr_...)
cadence:
type: string
enum: [daily, weekly, monthly]
description: How often the monitor runs
minute:
type: integer
minimum: 0
maximum: 59
description: Minute of the hour to run
hour:
type: integer
minimum: 0
maximum: 23
description: Hour of the day to run
day_of_week:
type: integer
minimum: 0
maximum: 6
description: Day of the week (0=Sunday, required for weekly)
day_of_month:
type: integer
minimum: 1
maximum: 28
description: Day of the month (required for monthly)
timezone:
type: string
description: "Timezone (e.g. 'UTC', 'America/New_York')"
webhook_url:
type: string
format: uri
description: URL to receive results each scheduled run
alert_rule:
type: string
enum: [each_run, only_on_change]
description: When to send alerts (default each_run)
output_format:
type: string
enum: [current_only, current_and_previous]
description: Output format (default current_only)
run_immediately:
type: boolean
description: Also run the monitor immediately after creation
input:
$ref: "#/components/schemas/EnrichmentInputData"
description: Optional fixed input data for the monitor
required:
- enrichment_id
- cadence
- minute
- hour
- timezone
examples:
daily_monitor:
summary: Daily monitor with change alerts
value:
enrichment_id: "enr_018f5b60-1234-7abc-89ab-0123456789ab"
cadence: daily
hour: 9
minute: 0
timezone: "UTC"
alert_rule: only_on_change
webhook_url: "https://your-server.com/webhook"
responses:
"201":
description: Monitor created
content:
application/json:
schema:
$ref: "#/components/schemas/Monitor"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"422":
$ref: "#/components/responses/UnprocessableEntity"
get:
summary: list monitors
description: List the account's monitors, newest first.
operationId: listMonitors
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const monitors = await riveter.monitors.list();
for (const monitor of monitors) {
console.log(monitor.id, monitor.schedule_summary);
}
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
for monitor in riveter.monitors.list():
print(monitor.id, monitor.schedule_summary)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
riveter.monitors.list.each do |monitor|
puts "#{monitor.id} #{monitor.schedule_summary}"
end
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
monitors, err := client.Monitors.List(context.Background())
for _, monitor := range monitors {
fmt.Println(monitor.ID, monitor.ScheduleSummary)
}
tags:
- Monitors
responses:
"200":
description: Monitors listed
content:
application/json:
schema:
type: object
properties:
monitors:
type: array
items:
$ref: "#/components/schemas/Monitor"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
/monitors/{id}:
get:
summary: get monitor
description: The monitor's schedule, webhook, and next run time.
operationId: getMonitor
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const monitor = await riveter.monitors.get("mon_YOUR_MONITOR_ID");
console.log(monitor.enabled, monitor.next_run_at);
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
monitor = riveter.monitors.get("mon_YOUR_MONITOR_ID")
print(monitor.enabled, monitor.next_run_at)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
monitor = riveter.monitors.get("mon_YOUR_MONITOR_ID")
puts "#{monitor.enabled} #{monitor.next_run_at}"
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
monitor, err := client.Monitors.Get(context.Background(), "mon_YOUR_MONITOR_ID")
fmt.Println(monitor.Enabled, monitor.NextRunAt)
tags:
- Monitors
parameters:
- name: id
in: path
required: true
description: The monitor id (mon_...)
schema:
type: string
responses:
"200":
description: The monitor
content:
application/json:
schema:
$ref: "#/components/schemas/Monitor"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
patch:
summary: update monitor
description: |
Pause, resume, or repoint a monitor. `enabled: false` pauses, `enabled: true` resumes; `webhook_url` replaces the delivery URL.
operationId: updateMonitor
x-mcp-open-world: true
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
// Pause the monitor:
const monitor = await riveter.monitors.update("mon_YOUR_MONITOR_ID", {
enabled: false,
});
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
# Pause the monitor:
monitor = riveter.monitors.update("mon_YOUR_MONITOR_ID", enabled=False)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
# Pause the monitor:
monitor = riveter.monitors.update("mon_YOUR_MONITOR_ID", enabled: false)
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
// Pause the monitor (Enabled is a pointer so `false` still serializes):
enabled := false
monitor, err := client.Monitors.Update(context.Background(), "mon_YOUR_MONITOR_ID",
riveter.UpdateMonitorParams{Enabled: &enabled})
tags:
- Monitors
parameters:
- name: id
in: path
required: true
description: The monitor id (mon_...)
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
enabled:
type: boolean
description: false pauses the monitor, true resumes it
webhook_url:
type: string
format: uri
examples:
pause:
summary: Pause
value:
enabled: false
resume:
summary: Resume
value:
enabled: true
responses:
"200":
description: The updated monitor
content:
application/json:
schema:
$ref: "#/components/schemas/Monitor"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"422":
$ref: "#/components/responses/UnprocessableEntity"
/monitors/{id}/runs:
get:
summary: list monitor runs
description: |
The monitor's run history, newest first. Fetch a specific run's data with [GET /runs/{id}/result](#tag/runs/get/runs/{id}/result). Supports `status`, `page`, and `per_page`.
operationId: listMonitorRuns
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const page = await riveter.monitors.runs("mon_YOUR_MONITOR_ID");
for await (const run of page) { // auto-pages through every result
console.log(run.id, run.status);
}
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
page = riveter.monitors.runs("mon_YOUR_MONITOR_ID")
for run in page.auto_paging_iter(): # pages through every result
print(run.id, run.status)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
page = riveter.monitors.runs("mon_YOUR_MONITOR_ID")
page.auto_paging_each do |run| # pages through every result
puts "#{run.id} #{run.status}"
end
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
ctx := context.Background()
page, err := client.Monitors.Runs(ctx, "mon_YOUR_MONITOR_ID", nil)
for {
for _, run := range page.Runs {
fmt.Println(run.ID, run.Status)
}
if !page.HasNextPage() {
break
}
page, err = page.NextPage(ctx)
}
tags:
- Monitors
parameters:
- name: id
in: path
required: true
description: The monitor id (mon_...)
schema:
type: string
- name: status
in: query
required: false
schema:
type: string
enum: [pending, enqueued, processing, success, stopped]
- name: page
in: query
required: false
schema:
type: integer
default: 1
- name: per_page
in: query
required: false
schema:
type: integer
default: 25
maximum: 100
responses:
"200":
description: Monitor runs listed
content:
application/json:
schema:
type: object
properties:
monitor_id:
type: string
runs:
type: array
items:
$ref: "#/components/schemas/RunListItem"
pagination:
$ref: "#/components/schemas/Pagination"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
/quick_search:
post:
summary: quick search
description: |
Run a web search and get structured results back **synchronously** — the response is the run with the results already in `output` (no polling, no webhook). Optionally filter to a date range with `date_start` / `date_end` (format `YYYY-MM-DD`); if only `date_start` is given, `date_end` defaults to today.
The result is also stored on the run, so it stays re-fetchable at [GET /runs/{id}/result](#tag/runs/get/runs/{id}/result).
For bulk searches — up to **100,000** per request, async with `webhook_url` support — use the legacy [POST /v1/web_search](./openapi.legacy.yaml) endpoint.
## Quick example
```bash
curl -X POST https://api.riveterhq.com/v1/quick_search \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "latest OpenAI news"}'
```
## Credit costs
- **0.04 credits** per search.
operationId: quickSearch
x-mcp-open-world: true
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const run = await riveter.quickSearch({ query: "Riveter data enrichment" });
console.log(run.output); // synchronous — results are already here
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
run = riveter.quick_search("Riveter data enrichment")
print(run.output) # synchronous — results are already here
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
run = riveter.quick_search(query: "Riveter data enrichment")
puts run.output # synchronous — results are already here
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
run, err := client.QuickSearch(context.Background(),
riveter.QuickSearchParams{Query: "Riveter data enrichment"})
fmt.Println(string(run.Output)) // synchronous — results are already here
tags:
- Tools
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
query:
type: string
description: The search query (simpler is better)
date_start:
type: string
description: "Optional start date filter, format YYYY-MM-DD"
date_end:
type: string
description: "Optional end date filter, format YYYY-MM-DD. Defaults to today if date_start is set"
run_key:
type: string
maxLength: 255
pattern: "^[A-Za-z0-9._~-]+$"
description: Optional idempotency key (becomes the run id "run_<run_key>")
dry_run:
$ref: "#/components/schemas/DryRunParam"
max_credits:
$ref: "#/components/schemas/MaxCreditsParam"
required:
- query
examples:
simple_search:
summary: A simple search
value:
query: "latest OpenAI news"
date_filtered_search:
summary: A search filtered to a date range
value:
query: "OpenAI GPT-4o mini"
date_start: "2024-07-01"
date_end: "2024-07-31"
responses:
"200":
description: Search completed — the run with the results in `output` (or, with dry_run, the credit estimate only)
content:
application/json:
schema:
oneOf:
- allOf:
- $ref: "#/components/schemas/Run"
- type: object
properties:
output:
description: 'The search results: `{ "results": [{ "title", "link", "snippet" }, ...], "knowledge_graph"? }`'
type: object
- $ref: "#/components/schemas/DryRunResult"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"409":
$ref: "#/components/responses/Conflict"
"422":
$ref: "#/components/responses/UnprocessableEntity"
/search_agent:
post:
summary: search agent
description: |
Ask a question and get an answer. This is meant for quick, relatively scoped one-off questions, like "What is the NAICS code for this company...". For more complex questions, use the `enrich/` endpoint.
This uses the same AI + web-tool loop that fills a single agent-mode cell in an enrichment (web search, web scraping, PDF reading, HTTP requests), with no enrichment setup.
The run is processed in the background while this request **long-polls up to `wait` seconds (default 50)** — most runs finish in time and return the answer inline in `output.result`. If the agent is still working when the budget elapses, the response comes back with `status: processing` and `output: null`; poll [GET /runs/{id}/result](#tag/runs/get/runs/{id}/result) (it long-polls too) until the run is terminal.
Pass `output_schema` (a JSON Schema object) to get `output.result` back as a structured object matching your schema instead of free text. Unanswerable questions return the string `"not found"`.
## Quick example
```bash
curl -X POST https://api.riveterhq.com/v1/search_agent \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "Who is the current CEO of Anthropic, and when did they take the role?"}'
```
## Credit costs
- **1 credit** per call (same as one agent-mode enrichment cell), charged when the agent completes. Failed runs are not charged.
operationId: searchAgent
x-mcp-open-world: true
tags:
- Tools
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
prompt:
type: string
maxLength: 50000
description: The question or task for the agent
output_schema:
type: object
description: 'Optional JSON Schema object for the answer, e.g. `{"type": "object", "properties": {"ceo_name": {"type": "string"}}}`. When set, `output.result` is an object matching this schema.'
wait:
type: integer
minimum: 0
maximum: 50
description: Seconds to hold this request waiting for the answer (default and max 50). Pass 0 to return immediately and poll GET /runs/{id}/result instead.
run_key:
type: string
maxLength: 255
pattern: "^[A-Za-z0-9._~-]+$"
description: Optional idempotency key (becomes the run id "run_<run_key>")
dry_run:
$ref: "#/components/schemas/DryRunParam"
max_credits:
$ref: "#/components/schemas/MaxCreditsParam"
required:
- prompt
examples:
free_text_question:
summary: A free-text question
value:
prompt: "Who is the current CEO of Anthropic, and when did they take the role?"
structured_answer:
summary: A structured answer via output_schema
value:
prompt: "Find the founding year and headquarters city of Anthropic"
output_schema:
type: object
properties:
founding_year:
type: integer
headquarters_city:
type: string
responses:
"201":
description: "The run — with the answer in `output.result` when it finished within `wait`, or `status: processing` and `output: null` when the agent is still working"
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/Run"
- type: object
properties:
output:
type: [object, "null"]
description: '`{ "result": <string or object> }` once the run finishes; null while it is still processing'
"200":
description: dry_run only — the flat per-call price; nothing was started
content:
application/json:
schema:
$ref: "#/components/schemas/DryRunResult"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"409":
$ref: "#/components/responses/Conflict"
"422":
$ref: "#/components/responses/UnprocessableEntity"
/scrape:
post:
summary: scrape
description: |
Scrape a webpage and return the text content **synchronously** — the only endpoint here that doesn't return a run to poll. Unchanged from the legacy API (response uses the legacy `request_status` format).
## Quick example
```bash
curl -X POST https://api.riveterhq.com/v1/scrape \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'
```
## Credit costs
- **With proxy**: 1/5 credit (0.20 credits)
- **Without proxy**: 1/20 credit (0.05 credits)
- **From cache**: 1/100 credit (0.01 credits)
## Proxy usage
Scraping is not guaranteed to succeed without a proxy. Some websites may block requests or require specific geographic locations. To use a proxy, include `proxy_country_code` with a two-character country code (e.g. 'us', 'gb', 'de').
## Caching
Recently scraped pages are cached to save credits (0.01 credits on a cache hit). Set `skip_cache: true` to always fetch fresh content.
operationId: scrape
x-mcp-open-world: true
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const page = await riveter.scrape({ url: "https://example.com" });
console.log(page.text); // synchronous — no run to poll
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
page = riveter.scrape("https://example.com")
print(page.text) # synchronous — no run to poll
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
page = riveter.scrape(url: "https://example.com")
puts page.text # synchronous — no run to poll
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
page, err := client.Scrape(context.Background(),
riveter.ScrapeParams{URL: "https://example.com"})
fmt.Println(page.Text) // synchronous — no run to poll
tags:
- Tools
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
url:
type: string
format: uri
description: The URL to scrape
proxy_country_code:
type: string
description: Optional two-character country code for proxy (e.g. 'us', 'gb', 'de')
pattern: "^[a-z]{2}$"
skip_cache:
type: boolean
description: Set to true to bypass cache and always fetch fresh content
default: false
dry_run:
$ref: "#/components/schemas/DryRunParam"
max_credits:
$ref: "#/components/schemas/MaxCreditsParam"
required:
- url
responses:
"200":
description: Webpage scraped successfully (or, with dry_run, the credit estimate only — minimum is the cache-hit price, maximum depends on proxy_country_code)
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ScrapeResponse"
- $ref: "#/components/schemas/DryRunResult"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"422":
$ref: "#/components/responses/UnprocessableEntity"
/account:
get:
summary: account
description: |
Information about the account associated with the API key: plan, credit balance, and the key's metadata. Unchanged from the legacy API (response uses the legacy `request_status` format).
operationId: getAccount
x-codeSamples:
- lang: typescript
label: TypeScript
source: |
import { Riveter } from "riveter-sdk";
const riveter = new Riveter(); // uses env RIVETER_API_KEY
const info = await riveter.account();
console.log(info.account.credit.balance);
- lang: python
label: Python
source: |
from riveter import Riveter
riveter = Riveter() # uses env RIVETER_API_KEY
info = riveter.account()
print(info.account.credit.balance)
- lang: ruby
label: Ruby
source: |
require "riveter"
riveter = Riveter::Client.new # uses env RIVETER_API_KEY
info = riveter.account
puts info.account.credit.balance
- lang: go
label: Go
source: |
import riveter "github.com/riveterhq/riveter-go"
client, err := riveter.NewClient() // uses env RIVETER_API_KEY
info, err := client.Account(context.Background())
fmt.Println(info.Account.Credit.Balance)
tags:
- Account
responses:
"200":
description: Account information retrieved successfully
content:
application/json:
schema:
type: object
properties:
request_status:
type: string
enum: [success]
message:
type: string
account:
$ref: "#/components/schemas/Account"
api_key_info:
$ref: "#/components/schemas/ApiKeyInfo"
required:
- request_status
- message
- account
- api_key_info
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
components:
securitySchemes:
ApiKeyAuth:
type: http
scheme: bearer
bearerFormat: API_KEY
description: API key authentication. Use 'Bearer YOUR_API_KEY' in the Authorization header.
x-scalar-secret-token: YOUR_API_KEY
schemas:
Run:
type: object
description: |
The uniform shape for every run, returned by kickoff endpoints and all /runs endpoints.
Related resource ids (enrichment_id, dataset_id, extraction_id, monitor_id) appear when they
apply to the run; kickoff responses may add a few endpoint-specific fields.
properties:
id:
type: string
description: The run id (run_...)
type:
type: string
enum:
[
enrichment,
dataset_build,
extraction,
scrape,
quick_search,
search_agent,
]
description: What kind of run this is
status:
type: string
enum: [pending, enqueued, processing, success, stopped]
progress:
$ref: "#/components/schemas/RunProgress"
credits_used:
type: number
description: Credits consumed so far
credit_detail:
$ref: "#/components/schemas/CreditDetail"
app_url:
type: string
format: uri
description: Link to view this run in the Riveter app
result_url:
type: string
format: uri
description: Where to fetch the run's output
started_at:
type: [string, "null"]
format: date-time
finished_at:
type: [string, "null"]
format: date-time
error:
type: [object, "null"]
description: Null unless the run hit an error
properties:
type:
type: string
message:
type: string
stopped_reason:
$ref: "#/components/schemas/StoppedReason"
enrichment_id:
type: string
description: Present when the run belongs to an enrichment
enrichment_name:
type: string
dataset_id:
type: string
description: Present on dataset-build runs
extraction_id:
type: string
description: Present on extraction runs
monitor_id:
type: string
description: Present on monitor-scheduled runs
webhook_url:
type: string
format: uri
description: Present when the run has a webhook configured
required:
- id
- type
- status
- progress
- credit_detail
- result_url
example:
id: "run_018f6a70-1234-7abc-89ab-0123456789ab"
type: "enrichment"
status: "processing"
progress:
percent_complete: 40
estimated_seconds_remaining: 90
elapsed_seconds: 60
completed_cells: 4
total_cells_expected: 10
not_found_cells: 0
credits_used: 2.5
credit_detail:
estimate:
minimum: 0
maximum: 10
charged_upfront: false
credits_used: 2.5
app_url: "https://app.riveterhq.com/runs/run_018f6a70-1234-7abc-89ab-0123456789ab"
result_url: "https://api.riveterhq.com/v1/runs/run_018f6a70-1234-7abc-89ab-0123456789ab/result"
started_at: "2026-01-15T12:00:00Z"
finished_at: null
error: null
enrichment_id: "enr_018f5b60-1234-7abc-89ab-0123456789ab"
enrichment_name: "My Enrichment"
DryRunParam:
type: boolean
default: false
description: |
Validate and price the request without creating or charging anything. Returns 200 with a DryRunResult
(credit estimate, credits_remaining, sufficient_credits) instead of starting a run.
MaxCreditsParam:
type: number
minimum: 0
description: |
Credit ceiling for this request. When the estimate's maximum exceeds it, the request is refused with
422 credit_cap_exceeded and nothing is charged.
CreditEstimate:
type: object
description: |
Kickoff-time credit estimate. `maximum` is the contract — the run never charges more.
`minimum` is 0 when the run can charge less (cells that short-circuit, unfound dataset rows refunded).
properties:
minimum:
type: number
description: Least the run can cost
maximum:
type: number
description: Most the run can cost
charged_upfront:
type: boolean
description: true when `maximum` is taken at kickoff (dataset builds, extractions); false when credits accrue as work completes
required:
- minimum
- maximum
- charged_upfront
CreditDetail:
type: object
description: |
The estimate made at kickoff next to what has been charged so far. `estimate` is null for runs
started before estimates existed. Dry runs and credit errors add `credits_remaining` and `sufficient_credits`.
properties:
estimate:
oneOf:
- $ref: "#/components/schemas/CreditEstimate"
- type: "null"
credits_used:
type: number
description: Credits charged so far (same value as the top-level credits_used)
credits_remaining:
type: number
description: Account balance. Only on dry runs and credit errors.
sufficient_credits:
type: boolean
description: Whether the balance covers `estimate.maximum`. Only on dry runs and credit errors.
required:
- estimate
- credits_used
DryRunResult:
type: object
description: |
Returned with `200` when a paid endpoint is called with `dry_run: true`. The request was validated
and priced; nothing was created or charged.
properties:
dry_run:
type: boolean
enum: [true]
type:
type: string
enum:
[
enrichment,
dataset_build,
extraction_discovery,
extraction,
scrape,
quick_search,
search_agent,
]
description: What would have been charged for
credit_detail:
$ref: "#/components/schemas/CreditDetail"
required:
- dry_run
- type
- credit_detail
example:
dry_run: true
type: "enrichment"
credit_detail:
estimate:
minimum: 0
maximum: 1920
charged_upfront: false
credits_used: 0
credits_remaining: 5000
sufficient_credits: true
RunProgress:
type: object
description: Completion estimate. Cell-based runs (enrichment, quick_search) also report cell counts.
properties:
percent_complete:
type: [number, "null"]
estimated_seconds_remaining:
type: [number, "null"]
elapsed_seconds:
type: [number, "null"]
completed_cells:
type: integer
description: Cell-based runs only
total_cells_expected:
type: integer
description: Cell-based runs only
not_found_cells:
type: [integer, "null"]
description: Cell-based runs only. Null while a run over 5,000 rows is in progress; set when it finishes.
RunListItem:
type: object
description: The slim run shape used by GET /runs and GET /monitors/{id}/runs.
properties:
id:
type: string
type:
type: string
enum:
[
enrichment,
dataset_build,
extraction,
scrape,
quick_search,
search_agent,
]
status:
type: string
enum: [pending, enqueued, processing, success, stopped]
enrichment_id:
type: [string, "null"]
enrichment_name:
type: [string, "null"]
row_count:
type: [integer, "null"]
credits_used:
type: number
error:
type: [object, "null"]
properties:
type:
type: string
message:
type: string
stopped_reason:
$ref: "#/components/schemas/StoppedReason"
created_at:
type: string
format: date-time
started_at:
type: [string, "null"]
format: date-time
finished_at:
type: [string, "null"]
format: date-time
app_url:
type: string
format: uri
result_url:
type: string
format: uri
StoppedReason:
type: string
enum: [user_request, credit_limit, error]
description: |
Why the run has status `stopped`. Only present on stopped runs.
`user_request`: stopped through the API or the app. `credit_limit`: the account ran out of credits.
`error`: the run failed (internal error, or every enrichment cell failed); `error.message` has the detail.
Pagination:
type: object
properties:
page:
type: integer
per_page:
type: integer
total_count:
type: integer
total_pages:
type: integer
EnrichmentInputData:
type: object
description: |
Keys are your source-data column headers. Values are arrays of strings (one per row).
Any column header name is allowed; all input columns must have the same array length.
Row caps: 10,000 rows with enrichment_id, 1,000 rows with an inline config.
additionalProperties:
type: array
description: One string per row (all input columns must have the same array length)
items:
type: string
maxItems: 10000
example:
"Company Name": ["Acme Corp", "Tech Solutions Inc"]
"Website": ["acme.com", "techsolutions.com"]
FormatDetails:
type: object
description: |
Format-specific options. Valid keys depend on the column `format`.
Only include keys that apply to your chosen format.
properties:
options:
type: array
items:
type: string
description: tag — allowed tag values (required for tag format)
allow_multiple:
type: boolean
description: tag — allow selecting more than one tag
descriptions:
type: object
description: tag — optional map from tag value to description (keys must be in `options`)
additionalProperties:
type: string
decimal_places:
type: integer
minimum: 0
maximum: 9
description: number — decimal places to round to
currency_code:
type: string
description: number — 3-letter currency code (mutually exclusive with percentage)
commas:
type: boolean
description: number — display thousands separators
percentage:
type: boolean
description: number — format as percentage (mutually exclusive with currency_code)
description:
type: string
description: json — natural-language schema description (use with or instead of `schema`)
schema:
type: object
description: |
json — JSON Schema object for structured output. Strict-mode requirements
(`additionalProperties: false`, full `required` arrays) are applied automatically.
Mark optional fields nullable (`"type": ["string", "null"]`). Not supported: `$ref`,
`oneOf`, `allOf`, `not`, non-object/array roots, nesting beyond 10 levels. `anyOf` is supported.
iso_8601:
type: boolean
description: date — output ISO 8601 (cannot combine with month/day/year/delimiter)
month:
type: string
enum: [M, MM, MMM, MMMM]
description: date — month format token
day:
type: string
enum: [D, DD, Do]
description: date — day format token
year:
type: string
enum: [YYYY, YY]
description: date — year format token
delimiter:
type: string
description: date — single-character delimiter between date parts
EnrichmentOutputColumnConfig:
type: object
description: |
Per-column enrichment config. **Agent mode** (default): include `prompt` and `contexts`.
**Tool-only mode**: set `tool` and its parameters (do not use `prompt`/`contexts`).
`run_when` / `run_when_config` apply in both modes.
properties:
prompt:
type: string
description: Agent instructions for this column (agent mode)
contexts:
type: array
description: Column headers used as input context (agent mode)
items:
type: string
tools:
type: array
description: "Agent tools (agent mode): web_search, scrape, pdf, image, http_request, check_urls, text_search_pdf. Default when omitted: web_search, scrape, pdf, http_request. `[]` = reasoning only, no tools."
items:
type: string
max_tool_calls:
type: integer
minimum: 0
maximum: 10
description: Agent mode — cap on tool calls per cell (default and max 10)
format:
type: string
enum: [text, number, url, email, tag, date, json, boolean]
format_details:
$ref: "#/components/schemas/FormatDetails"
run_when:
type: string
enum: [always, any_filled, all_filled, dynamic]
description: |
Per-row gate, checked before the cell runs (agent and tool-only columns). A skipped cell is
left empty and costs 0 credits. `always` (default) runs every row. `any_filled` runs when at
least one of the column's dependencies has a value — `contexts` for agent columns, column-mapped
tool params for tool-only columns (recommended default for chained columns: it skips rows where
the upstream column found nothing). `all_filled` runs only when every dependency has a value.
`dynamic` evaluates `run_when_config` rules against cell values — use it to branch on an
earlier output (e.g. run "CEO email" only when "CEO" is_not_empty, or skip an expensive
tool-only lookup unless "Company Size" text_contains "enterprise").
run_when_config:
type: object
description: |
Rules for `run_when: dynamic` (required then, ignored otherwise). The column runs for a row
only when the rules match: `match_mode: all` (default) = every rule matches, `any` = at least
one. Agent columns: every `column` a rule tests must also be listed in this column's `contexts`
— a rule on a column that is not a context sees an empty value. Tool-only columns: rule
columns become dependencies automatically; no `contexts` needed.
properties:
match_mode:
type: string
enum: [all, any]
description: How rules combine — `all` (default) or `any`
rules:
type: array
minItems: 1
description: Conditions evaluated against the row's cell values
items:
type: object
properties:
column:
type: string
description: Header of an input column or an earlier output column (agent columns must also list it in `contexts`; tool-only columns need nothing extra)
condition:
type: string
enum:
[
is_empty,
is_not_empty,
text_contains,
text_does_not_contain,
text_starts_with,
text_ends_with,
text_is_exactly,
]
description: |
`is_empty` / `is_not_empty` need no `value` ("not found" counts as empty). The `text_*`
conditions compare against `value`, case-insensitive, except `text_is_exactly` (exact,
case-sensitive).
value:
type: string
description: Comparison text for the `text_*` conditions (ignored for `is_empty` / `is_not_empty`)
required: [column, condition]
example:
match_mode: all
rules:
- column: "CEO"
condition: "is_not_empty"
- column: "Company Size"
condition: "text_contains"
value: "enterprise"
tool:
type: string
description: "Tool-only mode: scrape, web_search, pdf, image, code, LinkedIn tools, etc."
url:
type: string
description: Column header or static URL (tool-only)
query:
type: string
description: Column header or static query (tool-only, web_search)
date_start:
type: string
description: "Optional start date for filtering search results. Format: YYYY-MM-DD (tool-only, web_search)"
date_end:
type: string
description: "Optional end date filter. Format: YYYY-MM-DD. Defaults to today if date_start is provided (tool-only, web_search)"
code:
type: string
description: JavaScript source (tool-only, code tool)
args:
type: object
description: |
Named arguments for the code tool (tool-only). Keys are names referenced in your JavaScript
(e.g. `args.first`). Values are column headers (dynamic per row) or static strings.
proxy_country_code:
type: string
wait_longer:
type: boolean
skip_cache:
type: boolean
delete:
type: boolean
description: PATCH /enrichments/{id} only — set true to remove this column
EnrichmentOutputSpec:
type: object
description: |
Keys are output column headers. Values are per-column configuration objects.
Any output column name is allowed; see the per-column schema for all supported fields.
Columns run in dependency order: a column may list earlier output columns in `contexts`,
and `run_when` decides per row whether it runs at all.
additionalProperties:
$ref: "#/components/schemas/EnrichmentOutputColumnConfig"
example:
"Employee Count":
prompt: "Find the number of employees at this company"
contexts: ["Company Name", "Website"]
format: "number"
"Industry":
prompt: "What industry is this company in?"
contexts: ["Company Name"]
format: "tag"
format_details:
options: ["SaaS", "Fintech", "Healthcare", "Other"]
"CEO":
prompt: "Find the CEO's full name"
contexts: ["Company Name", "Website"]
"CEO Email":
prompt: "Find a work email address for this CEO"
contexts: ["Company Name", "CEO"]
format: "email"
run_when: "dynamic"
run_when_config:
rules:
- column: "CEO"
condition: "is_not_empty"
Extraction:
type: object
properties:
id:
type: string
description: Extraction id (ext_...)
name:
type: string
status:
type: string
enum: [discovering, ready, discovery_failed]
app_url:
type: string
format: uri
starting_url:
type: string
format: uri
goal_description:
type: string
output_record_json_schema:
type: [object, string, "null"]
description: The record schema you provided
required_keys:
type: array
items:
type: string
locked:
type: boolean
description: Locked plans can't be re-discovered
validation_passing:
type: [boolean, "null"]
description: Whether the last discovery validation passed
discovered_at:
type: [string, "null"]
format: date-time
run_credits_required:
type: number
description: Credits charged per run
Monitor:
type: object
properties:
id:
type: string
description: Monitor id (mon_...)
name:
type: string
enabled:
type: boolean
cadence:
type: string
enum: [daily, weekly, monthly]
minute:
type: integer
hour:
type: integer
day_of_week:
type: [integer, "null"]
day_of_month:
type: [integer, "null"]
timezone:
type: string
webhook_url:
type: [string, "null"]
format: uri
alert_rule:
type: string
enum: [each_run, only_on_change]
output_format:
type: string
enum: [current_only, current_and_previous]
next_run_at:
type: [string, "null"]
format: date-time
schedule_summary:
type: string
description: Human-readable schedule (e.g. "Daily at 9:00 UTC")
enrichment_id:
type: string
enrichment_name:
type: string
created_at:
type: string
format: date-time
has_input:
type: boolean
description: Whether the monitor carries fixed input data
ScrapeResponse:
type: object
description: Synchronous scrape result (legacy request_status format — endpoint unchanged from the legacy API).
properties:
request_status:
type: string
enum: [success]
text:
type: string
description: The extracted text content from the webpage
url:
type: string
format: uri
description: The URL that was scraped
base_url_for_links:
type: string
description: The base URL for resolving relative links
status_code:
type: integer
description: The HTTP status code returned by the server
possibly_blocked:
type: boolean
description: Present when the page may be blocked by anti-scraping measures
credit_used:
type: number
description: The number of credits consumed
credit_detail:
$ref: "#/components/schemas/CreditDetail"
riveter_app_link:
type: string
format: uri
description: Direct link to view this scrape in the Riveter application
Account:
type: object
properties:
uuid:
type: string
format: uuid
description: Unique identifier for the account
name:
type: string
description: Account name
plan:
type: string
enum: [free, starter, advanced, pro, enterprise]
description: Current billing plan
credit:
$ref: "#/components/schemas/Credit"
required:
- uuid
- name
- plan
- credit
Credit:
type: object
properties:
count:
type: integer
description: Current credit count
max:
type: integer
description: Maximum credits available
balance:
type: integer
description: Remaining credit balance
required:
- count
- max
- balance
ApiKeyInfo:
type: object
properties:
name:
type: string
description: Name of the API key
last_used_at:
type: [string, "null"]
format: date-time
description: When the API key was last used
created_by:
$ref: "#/components/schemas/User"
required:
- name
- last_used_at
- created_by
User:
type: object
properties:
uuid:
type: string
format: uuid
description: User's unique identifier
name:
type: string
description: User's full name
email:
type: string
format: email
description: User's email address
required:
- uuid
- name
- email
Error:
type: object
description: The uniform error body (all endpoints except 401 auth failures).
properties:
error:
type: object
properties:
type:
type: string
description: Machine-readable error type (bad_request, not_found, forbidden, duplicate_run_key, insufficient_credits, credit_cap_exceeded, validation, not_implemented, ...)
message:
type: string
description: Human-readable explanation
details:
description: Optional extra context (e.g. per-field validation errors)
credit_detail:
$ref: "#/components/schemas/CreditDetail"
description: On insufficient_credits and credit_cap_exceeded — the estimate plus credits_remaining and sufficient_credits
required:
- type
- message
required:
- error
examples:
- error:
type: "not_found"
message: "No run found with id run_018f6a70-..."
- error:
type: "credit_cap_exceeded"
message: "This run could cost up to 1920 credits, more than the max_credits of 1000"
credit_detail:
estimate:
minimum: 0
maximum: 1920
charged_upfront: false
credits_used: 0
credits_remaining: 5000
sufficient_credits: true
AuthError:
type: object
description: Authentication failures come from the shared auth layer and use the legacy shape.
properties:
request_status:
type: string
enum: [error]
message:
type: string
error_type:
type: string
enum: [unauthorized]
example:
request_status: "error"
message: "Invalid or missing API key"
error_type: "unauthorized"
responses:
BadRequest:
description: The request is malformed or names a conflicting parameter combination
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
Unauthorized:
description: Missing or invalid API key (legacy-shaped body — see the AuthError schema)
content:
application/json:
schema:
$ref: "#/components/schemas/AuthError"
Forbidden:
description: The API key's account may not access this resource or endpoint
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
NotFound:
description: No resource with that id on this account
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
Conflict:
description: Duplicate run_key or a run already in progress
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
UnprocessableEntity:
description: The request is valid but can't be executed (e.g. insufficient_credits, credit_cap_exceeded when max_credits is set, validation failure)
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
tags:
- name: Runs
description: |
The uniform lifecycle for every async operation — status, results, stop.
**The run** (returned by kickoffs and every `/runs` endpoint):
```json
{
"id": "run_018f6a70-1234-7abc-89ab-0123456789ab",
"type": "enrichment",
"status": "processing",
"progress": {
"percent_complete": 40,
"estimated_seconds_remaining": 90,
"elapsed_seconds": 60,
"completed_cells": 4,
"total_cells_expected": 10,
"not_found_cells": 0
},
"credits_used": 2.5,
"credit_detail": {
"estimate": { "minimum": 0, "maximum": 10, "charged_upfront": false },
"credits_used": 2.5
},
"app_url": "https://app.riveterhq.com/runs/run_018f6a70-...",
"result_url": "https://api.riveterhq.com/v1/runs/run_018f6a70-.../result",
"started_at": "2026-01-15T12:00:00Z",
"finished_at": null,
"error": null,
"enrichment_id": "enr_018f5b60-...",
"enrichment_name": "My Enrichment"
}
```
Related resource ids (`enrichment_id`, `dataset_id`, `extraction_id`, `monitor_id`) appear when they apply to the run. Kickoff responses may carry a few extra fields (e.g. `dataset_id`, `max_items` on dataset builds).
- name: Enrich
description: Enrich data and manage saved enrichment configurations
- name: Datasets
description: Generate rows from prompts, specs, or reusable templates
- name: Extractions
description: Reusable site scrape/extract recipes and their runs
- name: Tools
description: Synchronous and quick utilities — web search and scraping.
- name: Monitors
description: Scheduled enrichment runs with webhooks
- name: Account
description: Account and API key information