Bid Banana
PricingBid helpAPILog inStart free trial
Log inStart free trial
Bid Banana
SearchPricingBid helpBrowse bidsAPISupportFAQPrivacy PolicyTerms of UseAI Use Policy

This website and its content is copyright of The Bid Lab © 2026. The Bid Lab is a trademark of Bid Lab, LLC. All rights reserved.

← Back to the API

Developer reference

One endpoint, one JSON payload. Everything the v1 API accepts and returns.

  • Quickstart
  • Authentication
  • Parameters
  • Categories
  • Response
  • Bid object
  • Awarded
  • Errors
  • Limits

Quickstart

Every request is a POST to https://v1.api.bidbanana.com with your key in the JSON body. There is no path to append — POST to the bare host exactly as written and you get JSON back.

cURL
curl https://v1.api.bidbanana.com -d '{
  "api_key": "YOUR_API_KEY",
  "operation": "search",
  "keywords": "fruit",
  "category": "open",
  "state": "FL",
  "max_results": 25
}'

Authentication

Your API key travels in the request body as api_key — not as a header. Create and manage keys on your API Settings page. Keys start with bbsk-, you can hold up to 10 per account, any of them authenticates a call, and deleting one revokes it.

Keep your key server-side. Because the key sits in the request body, any call made from a browser exposes it to whoever is looking. Read it from an environment variable and call the API from your backend.

Request parameters

ParameterTypeRequiredDescription
api_keystringRequiredOne of your account's API keys, created on your API Settings page. Sent in the JSON body, not as a header. Never expose it in client-side code.
operationstringOptionalThe operation to perform. Defaults to "search". Send it explicitly so your client keeps working as operations are added.
keywordsstringRequiredSearch terms, separated by spaces, matched against titles, descriptions, agency names, solicitation IDs, industries and NAICS codes. Must be present, but may be an empty string: "" matches every record, so combined with state or updated_since_ms1970 it acts as a plain feed.
categorystringOptionalWhich dataset to search: "open" (default), "closed", or "awarded". Awarded results carry extra award-specific fields.
statestringOptionalTwo-letter US state code to limit results geographically, e.g. "TX". Omit it, or send an empty string, to search all 50 states.
updated_since_ms1970integerOptionalMilliseconds since the Unix epoch. Returns only records updated at their source after that moment, to whole-second precision — the field to use for a daily sync.
max_resultsintegerOptionalHow many results to return, as a JSON integer. Defaults to 10, ceiling 1,000. Larger values are clamped rather than rejected, and so is any value above what remains of your monthly allowance. There is no paging parameter — narrow the query instead.
only_all_keywordsbooleanOptionalSet true to require every keyword to appear. By default a result need only match some of them, ranked by frequency.

Open, closed, and awarded

The category parameter picks which dataset you search.

"open"

Active solicitations still accepting submissions. The default.

"closed"

Past solicitations no longer open. Same fields as open bids.

"awarded"

Awarded contracts — every bid field plus the winning vendors, award value, and contract terms.

Awarded search
curl https://v1.api.bidbanana.com -d '{
  "api_key": "YOUR_API_KEY",
  "operation": "search",
  "category": "awarded",
  "keywords": "road construction",
  "state": "TX",
  "max_results": 50
}'

Response

FieldTypeDescription
response_idstringUnique id for this response. Quote it in support requests.
response_time_ms1970numberWhen the response was generated, ms since epoch.
response_duration_msnumberHow long the request took to serve, in milliseconds.
api_specific_versionintegerBuild number of the API that produced this response.
categorystringEcho of the category searched — "open", "closed", or "awarded".
keywordsstringEcho of the keywords you sent.
statestring | nullEcho of the state filter, if you sent one.
updated_since_ms1970integer | nullEcho of the updated-since filter, if you sent one, rounded down to the second.
max_resultsintegerThe result limit actually applied. Lower than what you sent if you asked for more than 1,000 or more than your remaining monthly allowance.
only_all_keywordsbooleanEcho of the AND/OR flag — always present, false if you didn't send it.
found_countintegerHow many results are in this response. Always matches results.length.
resultsarrayThe matching records. See the bid object below.
errorstringPresent only when the request failed, alongside HTTP 400. See Errors.

The bid object

Each entry in results carries these fields. Any of them can be null when that detail couldn't be discovered for a given opportunity — that's normal, so read them defensively.

Identity
  • title · string
  • agency · string
  • solicitation_id · string | null
  • solicitation_type · string | null
    e.g. "Request for Proposal", "Invitation for Bid".
  • industry · string
  • naics_code · string
  • description · string | null
    Null when the source text is under 75 characters.
  • description_mini · string | null
    Short summary, good for list views.
Location
  • state · string
  • state_displayable · string
    Full state name, for display.
Dates
  • open_date · string
  • close_date · string
    Submission deadline, M/D/YYYY. The literal string "TBD" when the deadline is not yet known.
  • close_does_renew · boolean | null
    Whether this is a recurring opportunity.
  • questions_due_date · string | null
  • meeting_date · string | null
    Pre-bid meeting, if any.
  • meeting_is_mandatory · boolean | null
  • visit_date · string | null
    Site visit, if any.
  • visit_is_mandatory · boolean | null
  • award_date · string | null
  • project_duration · string | null
    e.g. "18-24 months".
  • last_updated_date · string
    M/D/YYYY, US Eastern.
  • last_updated_ms1970 · integer
    Pair with updated_since_ms1970 for syncing.
Money
  • budget · string | null
    Human-readable range.
  • budget_low_usd · integer | null
  • budget_high_usd · integer | null
  • budget_rationale · string | null
    How the AI estimate was derived.
Contact
  • contact_name · string | null
  • contact_email · string | null
  • contact_phone · string | null
  • contact_role · string | null
Links
  • bb_page_url · string
    The record's page on Bid Banana — under /bid/ for open and closed, /contract/ for awarded.
  • bb_page_url_partner · string
    Same page, with a token so your users can open it without logging in or hitting the anonymous view limit. Use this one when linking from your product.
  • login_is_required · boolean
  • payment_is_required · boolean
  • doc_files · array | null
    Attachments, each with file_download_url, file_name, file_title, file_type, file_size. ⚠️ file_download_url is a signed link that expires 15 minutes after the response is generated — download promptly, and never store or forward it.
  • _found_words · object
    Which fields matched, with {{keyword}} highlights. Keys come from title, description, description_mini, agency, agency_name, agency_rfp_id (the solicitation_id), industry, naics_code and budget_rationale.

Awarded contracts

An awarded search returns every field above plus the award-specific ones below — who won, for how much, and on what terms.

FieldTypeNotes
vendorsarray | nullThe winning vendor(s). Each has name, email, phone, address — any of which may be null.
award_typestring | nulle.g. "Single Award", "Multiple Award".
award_intent_datestring | nullNotice of intent to award; may precede award_date.
budget_usdinteger | nullThe actual awarded value. Distinct from the budget_low_usd–budget_high_usd estimate.
start_datestring | nullContract performance start.
end_datestring | nullContract performance end.
end_date_renewsstring | boolean | nullRenewal terms as text, e.g. "2 optional 1-year extensions", or false if it does not renew.
pricing_typestring | nulle.g. "Unit Price", "Firm-Fixed-Price (FFP) / Lump Sum".
procurement_typestring | nulle.g. "Full and Open Competition".
set_aside_typesarray | nulle.g. ["Small Business, Total Set-Aside"].
agency_emailstring | nullAgency-level contact, distinct from contact_email.
agency_phonestring | null
agency_addressstring | null

Errors

Every failure is an HTTP 400 with an error string in the body. A bad key, a missing keywords, an unknown category or operation, a body that isn't a JSON object, and an exhausted monthly allowance all come back the same way: status 400, no results, and a plain-English error saying what to fix.

Two other statuses exist. 413 means the request body was over 64 KB, and 500 means an unexpected fault on our side; both also carry an error string. Requests to any path other than the bare host are answered with a 307 redirect, so POST to https://v1.api.bidbanana.com exactly as written.

Treat any non-200 status as a failure and read error for the reason. A successful response never carries an error field.

StatusWhenerror
400api_key missing or not a stringyou must send an "api_key" string
400api_key not recognisedunauthorized: the "api_key" you provided lacks permission
400Subscription inactiveyour subscription does not allow API results …
400Monthly allowance used upyour "max_results_per_month" subscription limit (…) is exhausted for this month (…)
400keywords missing or not a stringyour query must supply a "keywords" string
400Unknown categoryunknown "category": "…". valid values: "open", "closed", "awarded".
400Unknown operationoperation_not_found
400Body is not a JSON objectinvalid_json_must_be_object
400Fault while running a searchinternal error: …
413Body larger than 64 KBpayload_too_large
500Unexpected server faultinternal_error
Error response · HTTP 400
{
  "response_id": "44967b18-0d34-4afc-a1af-240d3125eabc",
  "response_time_ms1970": 1769164147215.7112,
  "response_duration_ms": 0.42984999995678663,
  "error": "unauthorized: the \"api_key\" you provided lacks permission"
}

Usage limits

An API subscription includes 10,000 records per month. Records are counted per result returned, not per call — a request that returns 100 results uses 100 records, the same record returned twice counts twice, and open, closed and awarded searches all draw from the one allowance. The count resets on the 1st of each month (US Eastern).

As you near the end of the allowance, max_results is clamped to what remains — the echoed value in each response shows the limit that was applied. Once the allowance is used up, every search returns HTTP 400 with an error until the next month begins. Check your usage at any time on your API Settings page, and if you need more, talk to us about a higher limit or a partnership.

Each request returns at most 1,000 results and there is no paging parameter, so narrow large queries with state, category, or updated_since_ms1970. There is no per-request rate limit beyond the monthly allowance, though usage is monitored for abusive patterns.

Support

Questions, edge cases, or a field you wish we returned? Email [email protected] — include the response_id and we can trace the exact call.

← Back to the API