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
descriptionfield, 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
totalRecordsrecords, or on a short page (len(opportunitiesData) < limit). An empty200past the end looks the same as an empty200from a wrongoffset, so count what you received againsttotalRecordsrather than trusting the empty page. - The search returns open notices only.
status=archivedis 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. postedDateis 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 onnoticeId.
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.ois Solicitation,pPresolicitation,aAward Notice,rSources Sought,sSpecial Notice,kCombined Synopsis/Solicitation. Watchs: it is Special Notice, not solicitation. All nine codes are listed in the complete guide.solnum- Solicitation number searchncode- 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
- Start SAM.gov registration (if you have time to wait)
- Get instant API access (if you need to start now)
- Find decision maker contacts - 37,000+ contracting officers with direct phone & email
- View API documentation
Related Guides
- SAM.gov Response Structure Reference - JSON parsing patterns
- SAM.gov Rate Limits Explained - Access tiers and registration
- SAM.gov Capabilities vs Requirements - Choosing the right approach
- SAM.gov Exclusions Guide - Bulk sync, deduplication, compliance
- Federal Contracts API - Search FPDS prime contract transactions
- USASpending API Guide - Awarded-contract and federal spending data, endpoints and quirks