Get a Contract
const url = 'https://api-staging.fedwright.com/v1/contracts/example?expand=links';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'https://api-staging.fedwright.com/v1/contracts/example?expand=links' \ --header 'Authorization: Bearer <token>'One contract whole: the normalized fields, the fields as each source delivered them, which source supplied each field, and the upstream records that contributed to it (sources). A key of another kind is not found.
Data sources: FPDS, USAspending.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Query Parameters
Section titled “Query Parameters”Expand graph edges with the related record, relation, method, and confidence.
Comma-separated public field paths; up to 16 paths, 3 levels, and 48 response fields. A * selects every field at one level. Examples: piid,vendorName,agency.agencyName, placeOfPerformance.state,sources.source. detail is an opaque field. Unknown or excessive fields return a 400 problem with errors.shape.
Header Parameters
Section titled “Header Parameters”The ETag the client holds. A match returns 304.
Responses
Section titled “Responses”OK
A federal contract, IDV, or other transaction award whole: the summary’s fields, its dates, places, and parent vehicle, and the links and upstream records behind it.
object
The shared agency block of a record: the node in the federal agency tree and the office, agency, and department codes and names. It names a federal agency; it is not an organization of Fedwright’s customers.
object
The agency’s code, or null when the source gives none.
The agency’s name, or null when the source gives none.
The node of the federal agency tree, or null while the agency text or code is unresolved.
The department’s code, or null when the source gives none.
The department’s name, or null when the source gives none.
The office’s code, or null when the source gives none.
The office’s name, or null when the source gives none.
For an other transaction award, the agreement type code. Null otherwise.
The kind of award, which also names the dataset that serves it. contract is a definitive contract or an order against an indefinite delivery vehicle (IDV), idv is an IDV, ota is an other transaction agreement or an order against an other transaction IDV, and otidv is an other transaction IDV.
The code of the agency that identifies the award. The current awarding subtier may differ. Null when the source gives none.
The code of the awarding (purchasing) office. Null when the source gives none.
The extent competed code. Null when the source gives none.
The award’s current completion date. Null when the source gives none.
The latest valid description of the requirement. Null when the source gives none.
The source-specific fields, with normalized types and the publisher’s names. Null when there are none.
The effective date of the award, when performance starts. Null when the source gives none.
Every link of the record, with how each was matched and how confident the match is. Present only when the request sets expand=links.
A full graph edge returned with expand=links.
object
How sure the match is, from 0 to 1. A link on identical keys scores 1.
The related record’s dataset, as the API names it.
The related record’s identifier.
How the records were matched: exact-key for identical keys, normalized-key for keys that match once case, spacing, and punctuation are ignored, or fuzzy for keys one character apart.
How the records relate, such as award-of-opportunity.
For a record merged from several publishers, the source each merged top-level field was taken from. Null for a record from one publisher.
When Fedwright first saw the record.
The funding agency’s code. Null when the source states none.
The record’s identifier, its normalized natural key where the source has one.
For an IDV, the IDV type code. Null otherwise.
When Fedwright last saw the record in a source.
The record’s links to other records, in this dataset or another.
A related record in another dataset, or the same one, and how it relates.
object
The related record’s dataset, as the API names it.
The related record’s identifier.
How the records relate, such as award-of-opportunity.
The NAICS code of the award. Null when the source gives none.
For an other transaction award, the non-traditional participation code. Null otherwise.
For an order, the code of the agency that identifies the IDV it is placed against, as the source gives it. Null when the award is not an order.
The identifier of the IDV an order is placed against, derived from parentPiid and parentAgencyCode. The IDV itself may not be on record. Null when the award is not an order, or when either part is missing or can’t form an identifier.
For an order, the PIID of the IDV it is placed against, normalized as piid is, even when the IDV itself is not on record. Null when the award is not an order.
The award’s procurement instrument identifier (PIID), trimmed and in upper case, with each run of white space inside it one space.
The country, state, and postal code where the work is performed, each as the source gives it.
object
The country code, upper case. Null when the source gives none.
The state code, upper case. Null when the source gives none.
The ZIP or postal code. Null when the source gives none.
The product or service code (PSC) of the award. Null when the source gives none.
The version of the record’s shape.
The set-aside code of the award. Null when the source gives none.
The date of the base action, which can differ from the date of the latest modification. Null when the source gives none.
The solicitation identifier as the source writes it. Null when the source gives none.
One entry for each upstream record that contributed to the record.
An upstream record that contributed to a record.
object
When this publisher first delivered the record.
The version of the publisher’s format Fedwright read the record with.
When this publisher last delivered the record.
A source identifier, such as grantsgov.extract, for the kind of data the record came from.
The publisher, such as grantsgov; derived for a record Fedwright derived.
The publisher’s own identifier for the record, verbatim.
A link to the record at the publisher, or null when there is none.
The award’s ultimate completion date, including all options. Null when the source gives none.
When the record last changed.
The vendor’s name. Null when the source gives none.
The vendor’s unique entity identifier (UEI). Null when the source gives none.
Examples
A contract award detail
{ "agency": { "agencyCode": "4732", "agencyName": "Federal Acquisition Service", "agencyNodeId": "GSA.FAS", "departmentCode": "47", "departmentName": "General Services Administration", "officeCode": "47QDCA", "officeName": "FAS Acquisition Operations" }, "agreementType": null, "awardKind": "contract", "awardingAgencyCode": "4732", "awardingOfficeCode": "47QDCA", "baseAndAllOptionsValue": { "amount": "1250000.00", "currency": "USD" }, "competitionCode": "A", "currentCompletionDate": "2026-09-30T00:00:00Z", "description": "Cloud hosting and managed services for a case-management system.", "detail": null, "effectiveDate": "2025-10-01T00:00:00Z", "fieldSources": null, "firstSeenAt": "2026-03-02T04:00:12Z", "fundingAgencyCode": "4732", "id": "CONT_AWD_47QTCA24D0012_4732_-NONE-_-NONE-", "idvType": null, "lastSeenAt": "2026-03-09T04:00:31Z", "links": [], "naicsCode": "541512", "nonGovernmentalDollars": { "amount": "0.00", "currency": "USD" }, "nonTraditionalParticipation": null, "parentAgencyCode": null, "parentKey": null, "parentPiid": null, "piid": "47QTCA24D0012", "placeOfPerformance": { "country": "USA", "state": "VA", "zip": "22202" }, "pscCode": "DA01", "schemaVersion": 1, "setAsideCode": "SBA", "signedDate": "2025-09-26T00:00:00Z", "solicitationId": "47QDCA24Q0045", "sources": [ { "firstSeenAt": "2026-03-02T04:00:12Z", "formatVersionId": "v1", "lastSeenAt": "2026-03-09T04:00:31Z", "retrievalType": "usaspending.contracts", "source": "usaspending", "upstreamId": "CONT_AWD_47QTCA24D0012_4732_-NONE-_-NONE-", "upstreamUrl": "https://www.usaspending.gov/award/CONT_AWD_47QTCA24D0012_4732_-NONE-_-NONE-" } ], "totalObligatedAmount": { "amount": "480000.00", "currency": "USD" }, "ultimateCompletionDate": "2030-09-30T00:00:00Z", "updatedAt": "2026-03-09T04:00:31Z", "vendorName": "Harborview Analytics LLC", "vendorUei": "J4K7M2P9Q3R5"}Headers
Section titled “Headers”Strong entity tag of the returned resource.
The resource has not changed since the ETag in If-None-Match.
Headers
Section titled “Headers”Strong entity tag of the returned resource.
The request is malformed, or its schema refuses it: an unknown
property, a value of the wrong type or outside its bounds, a limit
above 200, or a sort outside the operation’s allow-list (Problem
type bad-request). errors names each field by its path, such as
nested.note, or $ for the body itself. The request is refused
before anything is read or written. A cursor that was altered, has
expired, or was issued for other filters, another sort, organization,
or user returns Problem type invalid-cursor.
RFC 9457 Problem Details.
object
An explanation specific to this occurrence of the problem. It is absent for server errors.
Validation errors, by field path.
object
A URI reference that identifies this occurrence of the problem, when the API provides one.
On rate-limited, the quota that is used up, such as org-day. On premium-quota-exceeded, org-premium-day. A 429 with an empty body does not name it.
On rate-limited from the API and on premium-quota-exceeded, when that quota starts again.
The HTTP status code of the response, from 400 to 599.
A short, human-readable summary of the problem. Use type to identify the kind of problem, not this text.
W3C trace ID of the failed request.
https://fedwright.com/problems/{code}, the same in every environment.
Example generated
{ "detail": "example", "errors": { "additionalProperty": [ "example" ] }, "instance": "example", "limit": "example", "resetsAt": "2026-04-15T12:00:00Z", "status": 1, "title": "example", "traceId": "example", "type": "https://example.com"}The access token is missing, expired, or invalid.
RFC 9457 Problem Details.
object
An explanation specific to this occurrence of the problem. It is absent for server errors.
Validation errors, by field path.
object
A URI reference that identifies this occurrence of the problem, when the API provides one.
On rate-limited, the quota that is used up, such as org-day. On premium-quota-exceeded, org-premium-day. A 429 with an empty body does not name it.
On rate-limited from the API and on premium-quota-exceeded, when that quota starts again.
The HTTP status code of the response, from 400 to 599.
A short, human-readable summary of the problem. Use type to identify the kind of problem, not this text.
W3C trace ID of the failed request.
https://fedwright.com/problems/{code}, the same in every environment.
Example generated
{ "detail": "example", "errors": { "additionalProperty": [ "example" ] }, "instance": "example", "limit": "example", "resetsAt": "2026-04-15T12:00:00Z", "status": 1, "title": "example", "traceId": "example", "type": "https://example.com"}The Problem type says which check refused the caller, when that is
about the caller’s own token, client, or role:
insufficient-scope when the token’s scopes (or an MCP grant’s) lack
the operation’s scope, with WWW-Authenticate: Bearer error="insufficient_scope", scope="..." (RFC 6750) naming the missing
scopes; client-not-allowed when the operation doesn’t admit the
caller’s kind of client, such as a machine client calling a user-only
operation; and insufficient-role when a confirmed member’s roles
don’t allow the action. Any other refusal is the generic forbidden,
which says nothing more. Another organization’s resource is 404
(not-found) instead, so no reason confirms that it exists. An owner
or administrator who sent no proof of a second factor gets
mfa-required. An operation
that needs an active subscription, when the organization has none,
returns subscription-inactive, or organization-suspended when
Fedwright suspended the organization. A suspended
organization also refuses invitations, new joins through an
invitation, and role changes with organization-suspended. Creating
or joining
an organization without a verified email returns email-unverified,
and accepting an invitation sent to another address returns
invitation-email-mismatch. A request from Fedwright’s web or mobile
app whose session acts for no organization yet returns
organization-required, except on getMe, createOrganization, and
acceptInvitation. Shared data needs the acting organization’s own
subscription; another organization’s doesn’t count.
RFC 9457 Problem Details.
object
An explanation specific to this occurrence of the problem. It is absent for server errors.
Validation errors, by field path.
object
A URI reference that identifies this occurrence of the problem, when the API provides one.
On rate-limited, the quota that is used up, such as org-day. On premium-quota-exceeded, org-premium-day. A 429 with an empty body does not name it.
On rate-limited from the API and on premium-quota-exceeded, when that quota starts again.
The HTTP status code of the response, from 400 to 599.
A short, human-readable summary of the problem. Use type to identify the kind of problem, not this text.
W3C trace ID of the failed request.
https://fedwright.com/problems/{code}, the same in every environment.
Example generated
{ "detail": "example", "errors": { "additionalProperty": [ "example" ] }, "instance": "example", "limit": "example", "resetsAt": "2026-04-15T12:00:00Z", "status": 1, "title": "example", "traceId": "example", "type": "https://example.com"}The resource does not exist, or belongs to another organization.
RFC 9457 Problem Details.
object
An explanation specific to this occurrence of the problem. It is absent for server errors.
Validation errors, by field path.
object
A URI reference that identifies this occurrence of the problem, when the API provides one.
On rate-limited, the quota that is used up, such as org-day. On premium-quota-exceeded, org-premium-day. A 429 with an empty body does not name it.
On rate-limited from the API and on premium-quota-exceeded, when that quota starts again.
The HTTP status code of the response, from 400 to 599.
A short, human-readable summary of the problem. Use type to identify the kind of problem, not this text.
W3C trace ID of the failed request.
https://fedwright.com/problems/{code}, the same in every environment.
Example generated
{ "detail": "example", "errors": { "additionalProperty": [ "example" ] }, "instance": "example", "limit": "example", "resetsAt": "2026-04-15T12:00:00Z", "status": 1, "title": "example", "traceId": "example", "type": "https://example.com"}A rate limit is used up. Direct API access counts against the
organization’s burst, daily, and monthly quotas, which all its machine
clients share; there is no client quota. A 429 comes in two shapes
with two header families, so read the response by what it carries.
A machine client’s calls can get a 429 with an empty body. It
has three headers: X-RateLimit-Limit, the
exhausted rule’s limit and window, such as 3000, 3000;w=2592000;
X-RateLimit-Remaining, what is left in that window (0 on a 429);
and X-RateLimit-Reset, the seconds until that window resets. It has
no Retry-After, no RateLimit headers, no Problem, and does not
name the quota. Read the quota from the window w:
w (seconds) |
Quota |
|---|---|
| 60 | Burst. |
| 86400 | Daily. |
| 2592000 | Monthly (the calendar month is reported as 30 days; X-RateLimit-Reset is exact). |
Wait X-RateLimit-Reset seconds before you retry. Refused requests
still count: a client that keeps calling after a 429 keeps adding to
the counter, so it does not regain budget inside the window. A tier
change takes effect within 10 minutes (checkout, the billing portal,
and grants alike), once the client gets a new access token.
Every other 429 has a Problem and Retry-After. The Problem type
is rate-limited for a used-up quota, or, for an ask, ai-budget-exceeded
or premium-quota-exceeded (below). On rate-limited, Retry-After is
the seconds until the used-up limit’s window resets, the latest if
several are. The Problem also carries limit, the quota that is used
up (the one that resets last, if several are), resetsAt, when it
starts again, and traceId. RateLimit-Policy and RateLimit describe only the
caller’s own limits, here and on every other response a limit applies
to. A request refused with rate-limited uses no quota. One
refused after that check, for its scope, role, subscription, or kind of
client, still counts, so once a quota is used up such a request gets
429 instead of its 403. A request that fails authentication is
not counted. An ask that the organization’s
AI spending budget can’t cover returns ai-budget-exceeded instead,
with Retry-After at the end of the budget’s window: that budget
counts what asks cost, not how many there are. An ask that the
organization’s premium units for the day can’t cover returns
premium-quota-exceeded, with limit (org-premium-day), resetsAt
(the next midnight UTC), and Retry-After. That is the plan’s
allowance: a plan with no premium units never covers an ask, and a
higher plan may. A refused ask uses no units.
RFC 9457 Problem Details.
object
An explanation specific to this occurrence of the problem. It is absent for server errors.
Validation errors, by field path.
object
A URI reference that identifies this occurrence of the problem, when the API provides one.
On rate-limited, the quota that is used up, such as org-day. On premium-quota-exceeded, org-premium-day. A 429 with an empty body does not name it.
On rate-limited from the API and on premium-quota-exceeded, when that quota starts again.
The HTTP status code of the response, from 400 to 599.
A short, human-readable summary of the problem. Use type to identify the kind of problem, not this text.
W3C trace ID of the failed request.
https://fedwright.com/problems/{code}, the same in every environment.
Examples
An ask the premium units can't cover
{ "limit": "org-premium-day", "resetsAt": "2026-11-08T00:00:00Z", "status": 429, "title": "Too Many Requests", "traceId": "4bf92f3577b34da6a3ce929d0e0e4736", "type": "https://fedwright.com/problems/premium-quota-exceeded"}A quota used up
{ "limit": "org-day", "resetsAt": "2026-11-08T00:00:00Z", "status": 429, "title": "Too Many Requests", "traceId": "4bf92f3577b34da6a3ce929d0e0e4736", "type": "https://fedwright.com/problems/rate-limited"}Headers
Section titled “Headers”For each limit in RateLimit-Policy, the requests r its window still
allows and the seconds t until the window resets, such as
"org-burst";r=7;t=40, "org-day";r=62;t=41000.
The caller’s rate limits, in the IETF HTTPAPI RateLimit header fields:
each limit’s name, its quota q, and its window w in seconds, such as
"org-burst";q=20;w=60, "org-day";q=100;w=86400.
Seconds to wait before retrying.
On a 429 with an empty body (and on the successful responses that carry
these headers), the exhausted rule’s limit and window w in seconds,
such as 3000, 3000;w=2592000. The window names the quota: 60 is burst,
86400 is daily, and 2592000 is monthly.
What is left in that rule’s window (0 on a 429).
Seconds until that rule’s window resets.
default
Section titled “default”Any other error. A request body over 64 KiB returns 413
(payload-too-large), and a request that takes longer than 10 seconds
(90 for askOpportunities) returns 503 (request-timeout). An invitation when the API can’t send mail returns 503
(mail-unavailable), and creates nothing. An unexpected failure returns 500 (internal-error)
with only type, title, status, and traceId.
RFC 9457 Problem Details.
object
An explanation specific to this occurrence of the problem. It is absent for server errors.
Validation errors, by field path.
object
A URI reference that identifies this occurrence of the problem, when the API provides one.
On rate-limited, the quota that is used up, such as org-day. On premium-quota-exceeded, org-premium-day. A 429 with an empty body does not name it.
On rate-limited from the API and on premium-quota-exceeded, when that quota starts again.
The HTTP status code of the response, from 400 to 599.
A short, human-readable summary of the problem. Use type to identify the kind of problem, not this text.
W3C trace ID of the failed request.
https://fedwright.com/problems/{code}, the same in every environment.
Example generated
{ "detail": "example", "errors": { "additionalProperty": [ "example" ] }, "instance": "example", "limit": "example", "resetsAt": "2026-04-15T12:00:00Z", "status": 1, "title": "example", "traceId": "example", "type": "https://example.com"}