# SAM.gov API — every US federal contract opportunity in clean JSON: solicitations, RFPs, sources-sought and award notices with the full solicitation text, the response deadline, the contracting office, the contracting officer's published contact details, NAICS/PSC codes and the attachment list. No api_key, no registration.

> Search every US federal contract opportunity by phrase — the demand side of a $700bn/year buyer. Each hit carries the response deadline, the buying department and office, the notice type (is this a heads-up, a live RFP, or an award?) and a 250-character preview of the solicitation text. Pair with `active_only=true` for things you can still bid on, or with notice type 'a' to see who won what.
> ReefAPI engine `sam-gov` · 3 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/sam-gov/v1/<action>` with a JSON body.
- **Auth:** header `x-api-key: <YOUR_REEFAPI_KEY>` — create one free (1,000 credits, no card): https://reefapi.com/signup
- **Response (every call):** `{ ok: boolean, data: ..., meta: { record_count, credits, ... }, error: { code, message } }` — branch on `ok`. Failed or blocked calls are free.
- **One key + one shared credit pool** across every ReefAPI API. Per-call credits are listed on each endpoint below.
- **Use it from an AI agent (MCP):** connect `https://api.reefapi.com/mcp` (remote streamable-http, `Authorization: Bearer <key>`) and your assistant can call these actions directly.

## Endpoints

### POST /sam-gov/v1/search — 1 credit
Search every US federal contract opportunity by phrase — the demand side of a $700bn/year buyer. Each hit carries the response deadline, the buying department and office, the notice type (is this a heads-up, a live RFP, or an award?) and a 250-character preview of the solicitation text. Pair with `active_only=true` for things you can still bid on, or with notice type 'a' to see who won what.

**Parameters:**
- `q` (string, required) — What to search for across the whole notice text (title, description and the indexed attachment text). By default this is treated as an EXACT PHRASE — the engine quotes it for you, because the raw upstream index otherwise matches single words anywhere and returns thousands of irrelevant notices (measured: 'web scraping' unquoted = 241,508 hits, quoted = 11). Set `phrase=false` for the loose all-words behaviour.
- `phrase` (boolean, optional, default true) — true (default): match `q` as an exact phrase. false: match notices containing EVERY word of `q` anywhere, in any order — far broader, and the right choice when you are casting a wide net rather than hunting one term. Whatever is actually sent upstream is echoed back as `query_sent`.
- `active_only` (boolean, optional, default false) — Only notices that are still OPEN (response deadline not passed, not archived). This filter runs upstream and bites hard — measured 485 → 2 on the same query — so use it when you want something you can still bid on, and leave it off when you are researching who bought what (award notices are inactive by definition).
- `notice_types` (array, optional) — Restrict to these notice-type codes (comma-separated string or list). Leave empty for all types. 'r' (Sources Sought) and 'p' (Presolicitation) are where a buyer is still shopping; 'a' (Award Notice) tells you who the incumbent is and when the contract runs out. [one of: p, k, r, o, g, s, i, a, u]
- `published_within_days` (integer, optional) — Only keep notices published within the last N days. ⚠️ SAM.gov has NO working server-side date filter (every documented date parameter is silently ignored — measured), so this is applied here, after fetching. That means it narrows what you receive but does NOT reduce upstream cost, and `total_matches` still reports the unfiltered upstream count. `filtered_out` says how many rows it removed.
- `sort` (enum, optional, default "newest") — Row order. 'newest' sorts by publish date (applied locally — upstream's '-publishDate' is broken and returns 2007 notices first). 'modified' is the raw upstream order. [one of: newest, modified, relevance]
- `limit` (integer, optional, default 25) — How many notices to return (1–500, default 25). The engine pages upstream 100 at a time to fill this.
- `offset` (integer, optional, default 0) — Skip this many matches before returning rows. offset + limit may not exceed 10000 — the index's own hard ceiling, which it publishes as `maxAllowedRecords`.

**Returns:** opportunities[]{id, url, title, solicitation_number, notice_type, notice_type_code, description_preview, published_at, modified_at, response_deadline, response_timezone, is_active, is_cancelled, buyer_name, buyer{department,office,office_code,office_location,chain[]}, awardee, relevance_score, revisions}, total_matches, returned, offset, query_sent, description_truncated, filtered_out

**Example request body:**
```json
{
  "q": "web scraping",
  "limit": 5
}
```

### POST /sam-gov/v1/opportunity — 1 credit
One notice in full: the COMPLETE solicitation text (search only ever returns the first 250 characters), the response deadline with its timezone, the set-aside type, NAICS and PSC classification, place of performance, and the contracting officer's name, e-mail and phone as published on the notice.

**Parameters:**
- `id` (string, required) — The 32-character SAM.gov notice id, as returned by `search` in `id`. A full https://sam.gov/opp/<id>/view URL also works — the id is read out of it.

**Returns:** opportunity{id, url, title, solicitation_number, notice_type_code, body, body_html, published_at, modified_at, response_deadline, response_timezone, archive_date, set_aside, naics_codes[], product_service_code, place_of_performance, contacts[]{type,name,email,phone}, is_active, is_archived, is_cancelled, revision, award}

**Example request body:**
```json
{
  "id": "ffabe21b876649d2a1669edb4858cd48"
}
```

### POST /sam-gov/v1/attachments — 1 credit
The documents attached to a notice — the statement of work, the pricing sheet, the Q&A, the amendment. Returns each file's name, MIME type, size, posting date and a direct public download URL. The real requirement usually lives in these PDFs, not in the notice body.

**Parameters:**
- `id` (string, required) — The 32-character SAM.gov notice id, as returned by `search` in `id`. A full https://sam.gov/opp/<id>/view URL also works — the id is read out of it.

**Returns:** attachments[]{id, name, kind, mime, size_bytes, posted_at, access, url}, returned

**Example request body:**
```json
{
  "id": "ffabe21b876649d2a1669edb4858cd48"
}
```

## More
- Try it live, no code: https://reefapi.com/playground?engine=sam-gov
- Human docs page: https://reefapi.com/docs/sam-gov
- Every ReefAPI API in one file (for your AI): https://reefapi.com/llms-full.txt
