Skip to content

Latest commit

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Tango Go SDK

Go ReferenceGo VersionLicense: MIT

In development — v0.1.0. Not yet at sibling-SDK parity. 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.

The sibling SDKs (tango-node and tango-python) are at v1.0.0. This Go SDK ships the full transport, error model, retry/rate-limit handling, webhook signing, and a curated subset of resource methods. More endpoints land each 0.x release; see CHANGELOG.md for what's shipped.

Features

  • Dynamic response shaping — request exactly the fields you need via 21 built-in shape presets or a custom comma-separated field selector.
  • Typed errors*AuthError, *NotFoundError, *ValidationError, *RateLimitError, *TimeoutError, *APIError, all composable via errors.As / errors.Is.
  • Smart retries — automatic backoff on 5xx / 408 / 429 / transport errors, honoring the server's Retry-After header.
  • Generic paginationPaginatedResponse[T] envelope + Iterator[T] that walks every page for you.
  • Webhook signing — drop-in HMAC-SHA256 verification, http.Handler middleware included.
  • Standard-library only — no transitive dependencies.

Installation

go get github.com/makegov/tango-go

Requires Go 1.23 or later (for Iterator[T].Seq()'s iter.Seq2).

Quick Start

Initialize the client

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()

List agencies

page, err:=client.ListAgencies(ctx, nil)
iferr!=nil {
log.Fatal(err)
}
for_, agency:=rangepage.Results {
fmt.Println(agency["agency_id"], agency["name"])
}

Get a specific agency

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")
iferr!=nil {
log.Fatal(err)
}
fmt.Println(*agency.Name) // "Department of Defense"

Search contracts with a minimal shape

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

Walk every contract matching a filter

iter:=client.IterateContracts(ctx, &tango.ListContractsOptions{
AwardingAgency: "9700",
FiscalYear: "2025",
})
foriter.Next() {
c:=iter.Item()
fmt.Println(c["piid"], c["total_contract_value"])
}
iferr:=iter.Err(); err!=nil {
log.Fatal(err)
}

Get a fully-shaped entity

entity, err:=client.GetEntity(ctx, "ABC123DEF456", &tango.GetEntityOptions{
Shape: tango.ShapeEntitiesComprehensive,
})

Authentication

With API key

client:=tango.NewClient(tango.WithAPIKey("your-key"))

From environment variable (TANGO_API_KEY)

export TANGO_API_KEY=your-key
client:=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.

Core Concepts

Dynamic Response Shaping

Every list and get endpoint accepts a shape parameter. The SDK ships 21 presets matching the Node/Python SDKs:

// Use a presetpage, _:=client.ListContracts(ctx, &tango.ListContractsOptions{
ListOptions: tango.ListOptions{Shape: tango.ShapeContractsMinimal},
})
// Or roll your ownpage, _=client.ListContracts(ctx, &tango.ListContractsOptions{
ListOptions: tango.ListOptions{Shape: "key,piid,recipient(uei,display_name)"},
})

Pagination

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)
foriter.Next() {
// iter.Item() is the next IDV
}

Rate limits

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.

Webhook verification

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 readvardeliverymap[string]anyjson.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)

Error Handling

Every error returned by the SDK can be unwrapped to one of the typed error types:

_, err:=client.GetAgency(ctx, "bogus")
varnf*tango.NotFoundErroriferrors.As(err, &nf) {
// handle 404 specifically
}
varrle*tango.RateLimitErroriferrors.As(err, &rle) {
log.Printf("rate limited; retry after %ds (limit type: %s)", rle.RetryAfter, rle.LimitType)
}
// Catch-allvarapiErr*tango.APIErroriferrors.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).

API Methods

The SDK exposes ~94 methods on *Client covering every endpoint in the sibling Node/Python SDKs. The most-used 15 are listed here; the full method-by-method reference lives in docs/API_REFERENCE.md.

ResourceListGetIterate
AgenciesListAgenciesGetAgency(typed: *AgencyRecord)
ContractsListContractsIterateContracts
EntitiesListEntitiesGetEntityIterateEntities
IDVsListIDVsGetIDVIterateIDVs
VehiclesListVehiclesGetVehicleIterateVehicles
OTAsListOTAsGetOTAIterateOTAs
OTIDVsListOTIDVsGetOTIDVIterateOTIDVs
OpportunitiesListOpportunitiesIterateOpportunities
NoticesListNoticesIterateNotices
ForecastsListForecastsIterateForecasts
GrantsListGrantsIterateGrants
ProtestsListProtestsGetProtest(typed: *ProtestRecord)IterateProtests
IT DashboardListItDashboardGetItDashboardIterateItDashboard
NAICS / PSCListNAICS / ListPSCGetNAICS / GetPSC
Webhooks (CRUD)ListWebhookEndpoints / ListWebhookAlertsGet… / Create… / Update… / Delete…

Sub-resources and lookups are also covered: ListEntityContracts / IDVs / OTAs / OTIDVs / Subawards / Lcats, 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, SearchOpportunityAttachments. Webhook signing: webhooks.Generate / Verify / Parse / VerifyRequest / Middleware.

See docs/API_REFERENCE.md for full signatures, filter fields, and quirks per method.

Project Structure

.
├── 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

Development

go test ./... # run unit tests
go test -race ./... # with the race detector
go vet ./... # static checks

Requirements

  • Go 1.23 or later.

Documentation

In-repo guides under docs/:

  • docs/ARCHITECTURE.md — package layout, request lifecycle, design rationale
  • docs/CLIENT.mdNewClient options, env vars, retry semantics, error model, rate-limit observability, iterator surface
  • docs/API_REFERENCE.md — method-by-method reference for every public method (~94)
  • docs/SHAPES.md — shape grammar, the 21 Shape* presets, Flat / FlatLists, trade-offs
  • docs/WEBHOOKS.md — receiving deliveries, CRUD methods, troubleshooting

External:

License

MIT — see LICENSE.

Support

Open an issue at https://github.com/makegov/tango-go/issues or email support@makegov.com.

Contributing

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.

About

Official Go SDK for the Tango API — federal contracts, IDVs, entities, opportunities, grants, vehicles, and more, with dynamic response shaping.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages