Skip to content

SAM.gov API Guide: Complete Developer Tutorial

Updated

Everything you need to know to integrate with SAM.gov's API for federal contract opportunities: registration, authentication, rate limits, and working code examples.

Quick Reference: SAM.gov provides free API access to federal contract opportunities. A basic key is instant and allows 10 requests/day; registered entities get 1,000/day. Registration takes up to 10 business days by SAM.gov's own statement, the API role is then approved by your entity's own administrator against a 5-business-day target, and the higher ceiling switches on 24 hours after that. Budget about three weeks.

Need Decision Maker Contacts?

Found opportunities on SAM.gov? Get verified contact info for agency contracting officers and program managers. Try GovCon Contacts →

What is SAM.gov?

The System for Award Management (SAM.gov) is the official U.S. government registry for entities doing business with the federal government. It consolidates multiple systems and provides APIs for accessing federal contracting opportunities.

SAM.gov provides access to:

  • Contract Opportunities - Active solicitations and awards
  • Entity Registration - Vendor registration and validation
  • Federal Hierarchy - Agency and sub-agency data
  • Exclusions Database - Debarred entities

SAM.gov API Registration Process

API Key Required (Basic Account - Instant)

Create free SAM.gov account → Generate key → 10 requests/day:

curl "https://api.sam.gov/prod/opportunities/v2/search?api_key=your_sam_key&limit=1&postedFrom=01/01/2026&postedTo=01/28/2026"

Entity Registration for 1,000/day (about 3 weeks)

Time Required: Up to 10 business days to become active (SAM.gov)

  • Register your business entity in SAM.gov
  • Obtain Unique Entity ID (UEI) - DUNS is no longer used
  • Complete entity validation process
  • Wait for approval

Step 2: API Access Request

Time Required: Your own Entity Administrator approves it, not the government. SAM's guidance is to escalate with an Entity Administrator Appointment Letter if there is no response after 5 business days (FSD KB0035972).

  • Submit role request for "Data Entry" role
  • Provide justification for API access
  • Wait for role approval
  • Generate API key through SAM.gov interface

Tip: Be specific in your justification. Explain your use case clearly (e.g.,"Building internal tool to monitor contract opportunities in NAICS 541511").

SAM.gov API Rate Limits

Rate Limits by Access Level

Access Level Requests Per Day How to Get It
Basic Account 10 SAM.gov account required (instant)
Entity User 1,000 Complete entity registration
Federal System User 10,000 Government .gov/.mil email + system account

Note: The 1,000/day limit works for most small-scale applications. For high-volume needs, consider caching responses or using bulk data downloads.

API Data Coverage

What the API Returns

  • Opportunity metadata - Title, agency, posted date, deadline
  • Solicitation numbers - Contract identifiers
  • Set-aside information - Small business designations
  • Contact information - Point of contact (when available)
  • Place of performance - Location data
  • NAICS/PSC codes - Industry classifications

What Requires Additional Steps

  • Full descriptions - A URL in the description field, fetched per notice, returned as HTML
  • Attachments - Require individual file requests
  • Award data - Often requires separate Award Notice lookup

Code Example: SAM.gov API

import requests
import json

# SAM.gov API endpoint
SAM_API_URL = "https://api.sam.gov/prod/opportunities/v2/search"
API_KEY = "your_sam_gov_api_key"  # Get from sam.gov after registration

def get_sam_opportunities():
    headers = {
        'Accept': 'application/json'
    }

    params = {
        'api_key': API_KEY,  # API key goes in query params
        'limit': 25,
        'postedFrom': '01/01/2026',
        'postedTo': '01/31/2026',
        'ptype': 'o'  # Solicitations only
    }

    response = requests.get(SAM_API_URL, headers=headers, params=params)
    response.raise_for_status()

    data = response.json()
    opportunities = data.get('opportunitiesData', [])

    for opp in opportunities:
        print(f"Title: {opp.get('title')}")
        print(f"Agency: {opp.get('fullParentPathName')}")
        print(f"Posted: {opp.get('postedDate')}")
        print(f"Deadline: {opp.get('responseDeadLine')}")
        print(f"Type: {opp.get('type')}")

        # Contact info is in an array
        contacts = opp.get('pointOfContact', [])
        if contacts:
            print(f"Contact: {contacts[0].get('email', 'N/A')}")
        print("---")

get_sam_opportunities()

Important: Pagination

offset is the page index, exactly as GSA's parameter table says ("Indicates the page index"). With limit=1000, the first page is offset=0 and the second is offset=1, not offset=1000. Measured 2026-10-28 on notices posted 10/27/2026 (totalRecords: 1261): offset=0 returned 1,000 records and offset=1 returned the other 261, with no overlap.

Send a record offset and the search looks capped at 1,000. offset=1000&limit=1000 asks for page 1,000, which is empty, and the endpoint answers 200 with zero records and the full totalRecords still in the body. That empty page is the source of the often-repeated "about 1,000 results" ceiling. Page by index and the whole result set comes back.

Three things worth designing around:

  • Stop when you hold totalRecords records, or on a short page (len(opportunitiesData) < limit). An empty 200 past the end looks the same as an empty 200 from a wrong offset, so count what you received against totalRecords rather than trusting the empty page.
  • The search returns open notices only. status=archived is rejected with a 400 ("The only supported value is Active"). A notice archived or cancelled before your query runs is not retrievable from this endpoint afterwards: SAM posted 1,271 notices on October 27, 2026, and the search for that day returned 1,261. The other 10 had closed first. Query often if you need every notice.
  • postedDate is date-only and can read a day late. For notices posted between 8 PM and midnight Eastern it sometimes carries the UTC date (3,142 notices, 5.7%, between September 1 and October 28, 2026), and the same notice can come back in two consecutive days' searches. Dedupe on noticeId.

Key Parameters

  • limit - Results per page (max 1000). Omit it and the API defaults to 1, so an otherwise correct call returns a single record.
  • postedFrom/postedTo - Date range (MM/DD/YYYY format, date only: a time component returns a 400). The span between them cannot exceed one year; a wider window is rejected.
  • ptype - Notice type. o is Solicitation, p Presolicitation, a Award Notice, r Sources Sought, s Special Notice, k Combined Synopsis/Solicitation. Watch s: it is Special Notice, not solicitation. All nine codes are listed in the complete guide.
  • solnum - Solicitation number search
  • ncode - NAICS code filter

Common Issues and Solutions

1. Registration Takes Too Long

Issue: Registration takes up to 10 business days, and the role request adds about 5 more.

Solution: Start with the public 10 requests/day while waiting. Use caching to make the most of limited calls.

2. Rate Limit Exceeded

Issue: Hit the daily limit during development.

Solution: Cache responses locally. The opportunities data doesn't change frequently, so a 1-hour cache is usually fine.

3. Need Full Descriptions

Issue: API returns metadata but not the full contract description.

Solution: The description field is a URL, not text. Fetch it at /opportunities/v1/noticedesc?noticeid=<id> with your API key appended, and expect HTML rather than plain text. That is a second request per notice, so a basic key is spent after ten descriptions. resourceLinks is a different field holding attachment URLs. It is not the description, and advice to read the attachments instead is the most common wrong turn here.

4. Need Award Data

Issue: Want to see who won similar contracts.

Solution: Search for "Award Notice" types in SAM.gov matching the solicitation number. SAM carries only the notice, not the full award record. For complete awarded-contract data (who won, how much, which agency, which vehicle), pull the Federal Contracts API, which exposes FPDS prime transactions; for agency spending and obligations, see the USASpending API guide.

When SAM.gov API Works Well

Good fit for:

  • Internal tools checking specific opportunities
  • Low-volume monitoring (under 100 checks/day)
  • Government agencies (10,000 requests/day with federal account)
  • Projects that can wait for registration
  • When you only need metadata, not full descriptions

When to Consider Alternatives

Consider other options if you need:

  • More than 1,000 requests per day
  • Full contract descriptions included in API response
  • Award data (who won, for how much) integrated
  • Faster setup (same-day API access)
  • Simpler JSON structure

Alternative: GovCon API

GovCon API provides SAM.gov data with some differences:

  • Instant access - No registration wait
  • Higher limits - 1,000 requests/hour
  • Descriptions included - Full text in API response
  • Award data - Integrated award fields included
  • Flat JSON - Simpler structure, easier parsing

Free tier: 50 requests a day. Developer is $19/mo, and the plan comparison says what each plan includes.

import requests

# GovCon API example
GOVCON_API_URL = "https://govconapi.com/api/v1/opportunities/search"
API_KEY = "your_govcon_api_key"

def get_opportunities():
    headers = {'Authorization': f'Bearer {API_KEY}'}
    params = {'naics': '541330', 'limit': 50}

    response = requests.get(GOVCON_API_URL, headers=headers, params=params)
    data = response.json()

    for opp in data.get('data', []):
        print(f"Title: {opp.get('title')}")
        print(f"Agency: {opp.get('agency')}")
        print(f"Description: {opp.get('description_text', '')[:200]}...")
        print("---")

get_opportunities()

Summary

SAM.gov's API is the official source for federal contract data. It's free, authoritative, and works well for low-volume use cases. The main tradeoffs are the registration wait time and rate limits.

For higher-volume needs or faster setup, third-party APIs like GovCon API provide alternatives with different tradeoffs (cost vs. convenience).

Next Steps

Official Resources

A REST API and MCP server for U.S. federal procurement data.

Get a free key, emailed instantly, or follow the quickstart.

Found something wrong, missing or confusing on this page?

Press Ctrl + Enter to send a note. Highlight text first and it travels with your note.