In development — v0.2.0. Pre-1.0. The public API may shift before v1.0.0. Pin to a specific tag if you depend on this in production.
Official Go SDK for the Tango API — federal contracts, IDVs, entities, opportunities, grants, vehicles, and more, with dynamic response shaping so you fetch only the fields you need.
This Go SDK ships the full transport, error model, retry/rate-limit handling and webhook signing, and covers the same public resources as the sibling tango-node and tango-python SDKs; see CHANGELOG.md for what's shipped.
- Dynamic response shaping — request exactly the fields you need via 29 built-in shape presets or a custom comma-separated field selector.
- Typed errors —
*AuthError,*NotFoundError,*ValidationError,*RateLimitError,*TimeoutError,*APIError, all composable viaerrors.As/errors.Is. - Smart retries — automatic backoff on 5xx / 408 / 429 / transport errors, honoring the server's
Retry-Afterheader. - Generic pagination —
PaginatedResponse[T]envelope +Iterator[T]that walks every page for you. - Webhook signing — drop-in HMAC-SHA256 verification,
http.Handlermiddleware included. - Standard-library only — no transitive dependencies.
go get github.com/makegov/tango-goRequires Go 1.23 or later (for Iterator[T].Seq()'s iter.Seq2).
import "github.com/makegov/tango-go"
client := tango.NewClient(
tango.WithAPIKey("your-api-key"),
)Or, when TANGO_API_KEY is set in the environment:
client := tango.NewClient()page, err := client.ListAgencies(ctx, nil)
if err != nil {
log.Fatal(err)
}
for _, agency := range page.Results {
fmt.Println(agency["agency_id"], agency["name"])
}GetAgency returns a typed *AgencyRecord — use the named fields, or agency.Extra for anything the server adds that isn't first-classed yet.
agency, err := client.GetAgency(ctx, "9700")
if err != nil {
log.Fatal(err)
}
fmt.Println(*agency.Name) // "Department of Defense"page, err := client.ListContracts(ctx, &tango.ListContractsOptions{
ListOptions: tango.ListOptions{Limit: 25, Shape: tango.ShapeContractsMinimal},
AwardingAgency: "9700",
AwardDateGte: "2024-01-01",
Sort: "award_date",
Order: "desc",
})iter := client.IterateContracts(ctx, &tango.ListContractsOptions{
AwardingAgency: "9700",
FiscalYear: "2025",
})
for iter.Next() {
c := iter.Item()
fmt.Println(c["piid"], c["total_contract_value"])
}
if err := iter.Err(); err != nil {
log.Fatal(err)
}entity, err := client.GetEntity(ctx, "ABC123DEF456", &tango.GetEntityOptions{
Shape: tango.ShapeEntitiesComprehensive,
})client := tango.NewClient(tango.WithAPIKey("your-key"))export TANGO_API_KEY=your-keyclient := tango.NewClient()You can also override the base URL via TANGO_BASE_URL or tango.WithBaseURL("https://staging.tango.makegov.com") — useful for staging environments.
Most list and get endpoints accept a shape parameter. The SDK ships 29 presets:
// Use a preset
page, _ := client.ListContracts(ctx, &tango.ListContractsOptions{
ListOptions: tango.ListOptions{Shape: tango.ShapeContractsMinimal},
})
// Or roll your own
page, _ = client.ListContracts(ctx, &tango.ListContractsOptions{
ListOptions: tango.ListOptions{Shape: "key,piid,recipient(uei,display_name)"},
})List endpoints support both page-based (Page) and cursor-based (Cursor) pagination. PaginatedResponse[T] carries both Next (the full URL) and Cursor (extracted from Next for keyset endpoints).
page, _ := client.ListIDVs(ctx, &tango.ListIDVsOptions{
ListOptions: tango.ListOptions{Limit: 50},
})
// Fetch the next page:
nextPage, _ := client.ListIDVs(ctx, &tango.ListIDVsOptions{
ListOptions: tango.ListOptions{Limit: 50, Cursor: page.Cursor},
})Or just iterate:
iter := client.IterateIDVs(ctx, nil)
for iter.Next() {
// iter.Item() is the next IDV
}After each request, client.RateLimitInfo() returns a snapshot of the rate-limit headers:
info := client.RateLimitInfo()
fmt.Printf("%d/%d remaining; resets in %ds\n", info.Remaining, info.Limit, info.ResetIn)The client also automatically retries 429s, honoring Retry-After.
import "github.com/makegov/tango-go/webhooks"
http.Handle("/tango-webhook", webhooks.Middleware(os.Getenv("WEBHOOK_SECRET"),
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// signature already verified; r.Body is reset and ready to read
var delivery map[string]any
json.NewDecoder(r.Body).Decode(&delivery)
// process delivery...
w.WriteHeader(http.StatusNoContent)
}),
))For lower-level use:
ok := webhooks.Verify(rawBody, r.Header.Get(webhooks.SignatureHeader), secret)Every error returned by the SDK can be unwrapped to one of the typed error types:
_, err := client.GetAgency(ctx, "bogus")
var nf *tango.NotFoundError
if errors.As(err, &nf) {
// handle 404 specifically
}
var rle *tango.RateLimitError
if errors.As(err, &rle) {
log.Printf("rate limited; retry after %ds (limit type: %s)", rle.RetryAfter, rle.LimitType)
}
// Catch-all
var apiErr *tango.APIError
if errors.As(err, &apiErr) {
log.Printf("status=%d data=%v", apiErr.StatusCode, apiErr.ResponseData)
}tango.IsRetryable(err) reports whether the SDK considers err retryable (it already retries internally — this helper is for callers building their own retry policies).
The SDK exposes about 140 methods on *Client covering the public resources the sibling Node/Python SDKs cover. The most-used 15 are listed here; the full method-by-method reference lives in docs/API_REFERENCE.md.
| Resource | List | Get | Iterate |
|---|---|---|---|
| Agencies | ListAgencies |
GetAgency (typed: *AgencyRecord) |
— |
| Contracts | ListContracts |
GetContract |
IterateContracts |
| Entities | ListEntities |
GetEntity |
IterateEntities |
| IDVs | ListIDVs |
GetIDV |
IterateIDVs |
| Vehicles | ListVehicles |
GetVehicle |
IterateVehicles |
| OTAs | ListOTAs |
GetOTA |
IterateOTAs |
| OTIDVs | ListOTIDVs |
GetOTIDV |
IterateOTIDVs |
| Opportunities | ListOpportunities |
GetOpportunity |
IterateOpportunities |
| Notices | ListNotices |
GetNotice |
IterateNotices |
| Forecasts | ListForecasts |
GetForecast |
IterateForecasts |
| Grants | ListGrants |
GetGrant |
IterateGrants |
| Budget accounts | ListBudgetAccounts |
GetBudgetAccount |
IterateBudgetAccounts |
| Protests | ListProtests |
GetProtest (typed: *ProtestRecord) |
IterateProtests |
| Contract appeals | ListContractAppeals |
GetContractAppeal (typed: *ContractAppealRecord) |
IterateContractAppeals |
| SLED opportunities / forecasts | ListSledOpportunities / ListSledForecasts |
GetSledOpportunity / GetSledForecast |
IterateSledOpportunities / IterateSledForecasts |
| Exclusions | ListExclusions |
GetExclusion |
IterateExclusions |
| DIBBS RFQs / RFPs / awards | ListDibbsRfqs / ListDibbsRfps / ListDibbsAwards |
GetDibbsRfq / GetDibbsRfp / GetDibbsAward |
IterateDibbs… |
| SBIR topics / solicitations | ListSbirTopics / ListSbirSolicitations |
GetSbirTopic / GetSbirSolicitation |
IterateSbir… |
| IT Dashboard | ListItDashboard |
GetItDashboard |
IterateItDashboard |
| NAICS / PSC | ListNAICS / ListPSC |
GetNAICS / GetPSC |
— |
| Webhooks (CRUD) | ListWebhookEndpoints / ListWebhookAlerts |
Get… / Create… / Update… / Delete… |
— |
Sub-resources and lookups are also covered: ListEntityContracts / IDVs / OTAs / OTIDVs / Subawards / Lcats, GetEntityBudgetFlows, ListContractSubawards / Transactions, GetBudgetAccountQuarters / Recipients, GetSubaward, ListSledOpportunityRevisions, GetSledCoverage, ListIDVAwards / ChildIDVs / Transactions / Lcats, ListAgencyAwardingContracts / FundingContracts, ListVehicleAwardees / Orders, ListOTIDVAwards, ListGsaElibraryContracts, ListBusinessTypes, ListOffices, ListDepartments (deprecated), ListMasSins, ListAssistanceListings, ListLcats, plus all three metrics getters (GetNAICSMetrics / GetPSCMetrics / GetEntityMetrics) and the dispatcher ListMetrics. Meta: Resolve, Validate, GetVersion, ListAPIKeys. Webhook signing: webhooks.Generate / Verify / Parse / VerifyRequest / Middleware.
See docs/API_REFERENCE.md for full signatures, filter fields, and quirks per method.
.
├── client.go # Client + NewClient + accessors
├── options.go # Functional options
├── transport.go # HTTP, auth, retries, rate-limit parsing
├── errors.go # Typed error tree
├── pagination.go # PaginatedResponse[T] + Iterator[T]
├── shapes.go # Shape preset constants + DefaultBaseURL
├── internal.go # ListOptions + generic helpers
├── version.go # SDK version constant
├── contracts.go # ListContracts + IterateContracts
├── entities.go # ListEntities + GetEntity + IterateEntities
├── idvs.go # ListIDVs + GetIDV + IterateIDVs
├── vehicles.go # ListVehicles + GetVehicle
├── opportunities.go # opportunities, notices, forecasts, grants
├── lookups.go # agencies, organizations, NAICS, PSC, subawards, version
├── resolve.go # Resolve + Validate
├── client_test.go # Transport + resource tests
├── webhooks/
│ ├── signing.go # Generate / Verify / Parse
│ ├── handler.go # VerifyRequest + Middleware
│ └── signing_test.go
├── examples/ # Runnable example programs
├── CHANGELOG.md
├── LICENSE
└── README.md
go test ./... # run unit tests
go test -race ./... # with the race detector
go vet ./... # static checks- Go 1.23 or later.
In-repo guides under docs/:
docs/ARCHITECTURE.md— package layout, request lifecycle, design rationaledocs/CLIENT.md—NewClientoptions, env vars, retry semantics, error model, rate-limit observability, iterator surfacedocs/API_REFERENCE.md— method-by-method reference for every public method (~94)docs/SHAPES.md— shape grammar, the 21Shape*presets,Flat/FlatLists, trade-offsdocs/WEBHOOKS.md— receiving deliveries, CRUD methods, troubleshooting
External:
- SDK reference: https://pkg.go.dev/github.com/makegov/tango-go
- API reference: https://docs.makegov.com
- Sibling SDKs:
@makegov/tango-node·tango-python
MIT — see LICENSE.
Open an issue at https://github.com/makegov/tango-go/issues or email support@makegov.com.
PRs welcome. Run go test ./... and go vet ./... before opening. For larger work, open an issue first so we can align on the API shape — public methods on *Client are a stability surface from 1.0.0 onward.