Skip to content

This is the staging environment's documentation. It is a draft and may change at any time.

Get a grant opportunity

GET
/grants/{id}
curl --request GET \
--url 'https://api-staging.fedwright.com/v1/grants/example?expand=links' \
--header 'Authorization: Bearer <token>'

One grant opportunity whole: the normalized fields, the publisher’s own fields as delivered, the history of its status changes, and the upstream records that contributed to it.

Data source: Grants.gov.

id
required
string
expand
string
Allowed values: links

Expand graph edges with the related record, relation, method, and confidence.

If-None-Match
string

The ETag the client holds. A match returns 304.

OK

Media typeapplication/json

A grant opportunity from Grants.gov whole: the summary’s fields, its funding, eligibility, and dates, its retained versions, and the links and upstream records behind it.

object
agency
One of:
null
agencyCode

The code of the agency that offers the opportunity. Null when Grants.gov gives none.

null | string
agencyName

The name of the agency that offers the opportunity. Null when Grants.gov gives none.

null | string
archiveDate

The date the opportunity is archived. Null when Grants.gov gives none.

null | string format: date-time
assistanceListingNumbers

The assistance listing numbers (formerly CFDA numbers) the opportunity falls under.

Array<string>
awardCeiling

The largest award the opportunity expects to make, in US dollars. Null when Grants.gov gives none or states none.

null | integer | string
/^-?(?:0|[1-9]\d*)$/
awardFloor

The smallest award the opportunity expects to make, in US dollars. Null when Grants.gov gives none.

null | integer | string
/^-?(?:0|[1-9]\d*)$/
category

The opportunity category code, as Grants.gov gives it, such as D. Null when Grants.gov gives none.

null | string
categoryExplanation

Grants.gov’s text explaining the funding activity category. Null when it gives none.

null | string
closeDate

The date applications close. Null when Grants.gov gives none.

null | string format: date-time
closeDateExplanation

Grants.gov’s text explaining the close date. Null when it gives none.

null | string
costSharingRequired

True when the opportunity requires cost sharing or matching funds, false when it does not. Null when Grants.gov states neither.

null | boolean
description

The opportunity’s description, as Grants.gov gives it. Null when it gives none.

null | string
detail

The source-specific fields, with normalized types and the publisher’s names. Null when there are none.

null | object
eligibilityDetails

Grants.gov’s additional information on who is eligible. Null when it gives none.

null | string
eligibleApplicantTypes

The codes for the types of applicants that are eligible, as Grants.gov gives them.

Array<string>
estimatedAwardDate

The estimated date of the awards. Null when Grants.gov gives none.

null | string format: date-time
estimatedProjectStartDate

The estimated start date of the funded projects. Null when Grants.gov gives none.

null | string format: date-time
estimatedSynopsisCloseDate

For a forecast, the estimated date the synopsis closes. Null when Grants.gov gives none.

null | string format: date-time
estimatedSynopsisPostDate

For a forecast, the estimated date the synopsis is posted. Null when Grants.gov gives none.

null | string format: date-time
estimatedTotalProgramFunding

The estimated total funding for the program, in US dollars. Null when Grants.gov gives none.

null | integer | string
/^-?(?:0|[1-9]\d*)$/
expandedLinks

Every link of the record, with how each was matched and how confident the match is. Present only when the request sets expand=links.

Array<object>

A full graph edge returned with expand=links.

object
confidence
required

How sure the match is, from 0 to 1. A link on identical keys scores 1.

number | string
/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/
dataset
required

The related record’s dataset, as the API names it.

string
id
required

The related record’s identifier.

string
method
required

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.

string
relation
required

How the records relate, such as award-of-opportunity.

string
expectedNumberOfAwards

The number of awards expected. Null when Grants.gov gives none.

null | integer | string
/^-?(?:0|[1-9]\d*)$/
fieldSources

For a record merged from several publishers, the source each merged top-level field was taken from. Null for a record from one publisher.

null | object
firstSeenAt

When Fedwright first saw the record.

string format: date-time
fiscalYear

The fiscal year of the opportunity. Null when Grants.gov gives none.

null | integer | string
/^-?(?:0|[1-9]\d*)$/
fundingActivityCategories

The funding activity category codes, as Grants.gov gives them.

Array<string>
fundingInstruments

The funding instrument type codes, such as the code for a cooperative agreement, as Grants.gov gives them.

Array<string>
history

The retained versions of the opportunity, oldest first.

Array<object>

A pointer to one retained version of a grant opportunity.

object
changedAt
required

When Fedwright recorded this version.

string format: date-time
status
required

The opportunity’s status in this version.

string
version
required

The version number, 1 for the first.

integer | string
/^-?(?:0|[1-9]\d*)$/
id

The record’s identifier, its normalized natural key where the source has one.

string
lastSeenAt

When Fedwright last saw the record in a source.

string format: date-time
lastUpdatedDate

When Grants.gov last updated the opportunity. Null when it gives none.

null | string format: date-time
links

The record’s links to other records, in this dataset or another.

Array<object>

A related record in another dataset, or the same one, and how it relates.

object
dataset
required

The related record’s dataset, as the API names it.

string
id
required

The related record’s identifier.

string
relation
required

How the records relate, such as award-of-opportunity.

string
notSeenSince

When a complete Grants.gov snapshot first confirmed that the opportunity was no longer listed. Null while it is listed.

null | string format: date-time
opportunityId

The Grants.gov opportunity identifier, which is also the record’s identifier.

string
opportunityNumber

The funding opportunity number the agency assigns. Null when Grants.gov gives none.

null | string
postDate

The date the opportunity was posted. Null when Grants.gov gives none.

null | string format: date-time
schemaVersion

The version of the record’s shape.

integer | string
/^-?(?:0|[1-9]\d*)$/
sources

One entry for each upstream record that contributed to the record.

Array<object>

An upstream record that contributed to a record.

object
firstSeenAt
required

When this publisher first delivered the record.

string format: date-time
formatVersionId
required

The version of the publisher’s format Fedwright read the record with.

null | string
lastSeenAt
required

When this publisher last delivered the record.

string format: date-time
retrievalType
required

A source identifier, such as grantsgov.extract, for the kind of data the record came from.

string
source
required

The publisher, such as grantsgov; derived for a record Fedwright derived.

string
upstreamId
required

The publisher’s own identifier for the record, verbatim.

string
upstreamUrl
required

A link to the record at the publisher, or null when there is none.

null | string format: uri
status

The opportunity’s lifecycle status: archived, closed, forecasted, posted, or unknown until its history has been read.

string
title

The opportunity’s title. Null when Grants.gov gives none.

null | string
updatedAt

When the record last changed.

string format: date-time

Examples

Exampledefault

A grant detail

{
"agency": {
"agencyCode": "7529",
"agencyName": "Administration for Children and Families",
"agencyNodeId": "HHS.ACF",
"departmentCode": "75",
"departmentName": "Department of Health and Human Services",
"officeCode": "75ACF1",
"officeName": "Office of Grants Management"
},
"agencyCode": "HHS-ACF",
"agencyName": "Administration for Children and Families",
"archiveDate": "2026-05-15T00:00:00Z",
"assistanceListingNumbers": [
"93.575"
],
"awardCeiling": 500000,
"awardFloor": 100000,
"category": "D",
"categoryExplanation": "Child care and early education.",
"closeDate": "2026-04-15T23:59:00Z",
"closeDateExplanation": "Applications are due by 11:59 p.m. Eastern on the closing date.",
"costSharingRequired": false,
"description": "Funds child care access for parents enrolled in postsecondary programs at eligible institutions.",
"detail": null,
"eligibilityDetails": "State, county, and tribal governments; nonprofits with 501(c)(3) status.",
"eligibleApplicantTypes": [
"State governments",
"Nonprofits having a 501(c)(3) status"
],
"estimatedAwardDate": "2026-08-01T00:00:00Z",
"estimatedProjectStartDate": "2026-10-01T00:00:00Z",
"estimatedSynopsisCloseDate": "2026-06-01T00:00:00Z",
"estimatedSynopsisPostDate": "2026-05-01T00:00:00Z",
"estimatedTotalProgramFunding": 5000000,
"expectedNumberOfAwards": 10,
"fieldSources": null,
"firstSeenAt": "2026-03-02T04:00:12Z",
"fiscalYear": 2026,
"fundingActivityCategories": [
"Education"
],
"fundingInstruments": [
"Grant"
],
"history": [
{
"changedAt": "2026-03-04T15:30:00Z",
"status": "posted",
"version": 1
}
],
"id": "350000",
"lastSeenAt": "2026-03-09T04:00:31Z",
"lastUpdatedDate": "2026-02-20T16:30:00Z",
"links": [],
"notSeenSince": "2026-03-09T04:00:31Z",
"opportunityId": "350000",
"opportunityNumber": "HHS-2026-ACF-OCC-0021",
"postDate": "2026-02-02T14:00:00Z",
"schemaVersion": 1,
"sources": [
{
"firstSeenAt": "2026-03-02T04:00:12Z",
"formatVersionId": "v1",
"lastSeenAt": "2026-03-09T04:00:31Z",
"retrievalType": "grantsgov.extract",
"source": "grantsgov",
"upstreamId": "350000",
"upstreamUrl": "https://www.grants.gov/search-results-detail/350000"
}
],
"status": "posted",
"title": "Child Care Access Means Parents in School Program",
"updatedAt": "2026-03-09T04:00:31Z"
}
ETag
string

Strong entity tag of the returned resource.

The resource has not changed since the ETag in If-None-Match.

ETag
string

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.

Media typeapplication/problem+json

RFC 9457 Problem Details.

object
detail

An explanation specific to this occurrence of the problem. It is absent for server errors.

string
errors

Validation errors, by field path.

object
key
additional properties
Array<string>
instance

A URI reference that identifies this occurrence of the problem, when the API provides one.

string
limit

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.

string
resetsAt

On rate-limited from the API and on premium-quota-exceeded, when that quota starts again.

string format: date-time
status
required

The HTTP status code of the response, from 400 to 599.

integer
>= 400 <= 599
title
required

A short, human-readable summary of the problem. Use type to identify the kind of problem, not this text.

string
traceId
required

W3C trace ID of the failed request.

string
type
required

https://fedwright.com/problems/{code}, the same in every environment.

string format: uri

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.

Media typeapplication/problem+json

RFC 9457 Problem Details.

object
detail

An explanation specific to this occurrence of the problem. It is absent for server errors.

string
errors

Validation errors, by field path.

object
key
additional properties
Array<string>
instance

A URI reference that identifies this occurrence of the problem, when the API provides one.

string
limit

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.

string
resetsAt

On rate-limited from the API and on premium-quota-exceeded, when that quota starts again.

string format: date-time
status
required

The HTTP status code of the response, from 400 to 599.

integer
>= 400 <= 599
title
required

A short, human-readable summary of the problem. Use type to identify the kind of problem, not this text.

string
traceId
required

W3C trace ID of the failed request.

string
type
required

https://fedwright.com/problems/{code}, the same in every environment.

string format: uri

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.

Media typeapplication/problem+json

RFC 9457 Problem Details.

object
detail

An explanation specific to this occurrence of the problem. It is absent for server errors.

string
errors

Validation errors, by field path.

object
key
additional properties
Array<string>
instance

A URI reference that identifies this occurrence of the problem, when the API provides one.

string
limit

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.

string
resetsAt

On rate-limited from the API and on premium-quota-exceeded, when that quota starts again.

string format: date-time
status
required

The HTTP status code of the response, from 400 to 599.

integer
>= 400 <= 599
title
required

A short, human-readable summary of the problem. Use type to identify the kind of problem, not this text.

string
traceId
required

W3C trace ID of the failed request.

string
type
required

https://fedwright.com/problems/{code}, the same in every environment.

string format: uri

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.

Media typeapplication/problem+json

RFC 9457 Problem Details.

object
detail

An explanation specific to this occurrence of the problem. It is absent for server errors.

string
errors

Validation errors, by field path.

object
key
additional properties
Array<string>
instance

A URI reference that identifies this occurrence of the problem, when the API provides one.

string
limit

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.

string
resetsAt

On rate-limited from the API and on premium-quota-exceeded, when that quota starts again.

string format: date-time
status
required

The HTTP status code of the response, from 400 to 599.

integer
>= 400 <= 599
title
required

A short, human-readable summary of the problem. Use type to identify the kind of problem, not this text.

string
traceId
required

W3C trace ID of the failed request.

string
type
required

https://fedwright.com/problems/{code}, the same in every environment.

string format: uri

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.

Media typeapplication/problem+json

RFC 9457 Problem Details.

object
detail

An explanation specific to this occurrence of the problem. It is absent for server errors.

string
errors

Validation errors, by field path.

object
key
additional properties
Array<string>
instance

A URI reference that identifies this occurrence of the problem, when the API provides one.

string
limit

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.

string
resetsAt

On rate-limited from the API and on premium-quota-exceeded, when that quota starts again.

string format: date-time
status
required

The HTTP status code of the response, from 400 to 599.

integer
>= 400 <= 599
title
required

A short, human-readable summary of the problem. Use type to identify the kind of problem, not this text.

string
traceId
required

W3C trace ID of the failed request.

string
type
required

https://fedwright.com/problems/{code}, the same in every environment.

string format: uri

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"
}
RateLimit
string

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.

RateLimit-Policy
string

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.

Retry-After
integer

Seconds to wait before retrying.

X-RateLimit-Limit
string

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.

X-RateLimit-Remaining
integer

What is left in that rule’s window (0 on a 429).

X-RateLimit-Reset
integer

Seconds until that rule’s window resets.

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.

Media typeapplication/problem+json

RFC 9457 Problem Details.

object
detail

An explanation specific to this occurrence of the problem. It is absent for server errors.

string
errors

Validation errors, by field path.

object
key
additional properties
Array<string>
instance

A URI reference that identifies this occurrence of the problem, when the API provides one.

string
limit

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.

string
resetsAt

On rate-limited from the API and on premium-quota-exceeded, when that quota starts again.

string format: date-time
status
required

The HTTP status code of the response, from 400 to 599.

integer
>= 400 <= 599
title
required

A short, human-readable summary of the problem. Use type to identify the kind of problem, not this text.

string
traceId
required

W3C trace ID of the failed request.

string
type
required

https://fedwright.com/problems/{code}, the same in every environment.

string format: uri

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"
}