npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@makegov/tango-node

v0.3.0

Published

Official Node.js SDK for the Tango API – dynamic response shaping, typed models, and full endpoint coverage.

Downloads

58

Readme

Tango Node SDK

A modern Node.js SDK for the Tango API, featuring dynamic response shaping, strong TypeScript types, and full coverage of the core Tango endpoints.

This is the Node/TypeScript port of the official Tango Python SDK.

Features

  • Dynamic Response Shaping – Ask Tango for exactly the fields you want using a simple shape syntax.
  • Type-Safe by Design – Shape strings are validated against Tango schemas and mapped to generated TypeScript types.
  • Comprehensive API Coverage – Agencies, business types, entities, contracts, vehicles, IDVs, forecasts, opportunities, notices, grants, and webhooks.
  • Flexible Data Access – Plain JavaScript objects backed by runtime validation and parsing, materialized via the dynamic model pipeline.
  • Modern Node – Built for Node 18+ with native fetch and ESM-first design.
  • Tested Against the Real API – Integration tests (mirroring the Python SDK) keep behavior aligned.

Installation

Requirements: Node 18 or higher.

npm install @makegov/tango-node
# or
yarn add @makegov/tango-node
# or
pnpm add @makegov/tango-node

Quick Start

Initialize the client

import { TangoClient } from "@makegov/tango-node";

const client = new TangoClient({
  apiKey: process.env.TANGO_API_KEY,
  // baseUrl: "https://tango.makegov.com", // default
});

List agencies

const agencies = await client.listAgencies();

for (const agency of agencies.results) {
  console.log(agency.code, agency.name);
}

Get a specific agency

const treasury = await client.getAgency("2000"); // Treasury
console.log(treasury.name, treasury.department?.name);

Search contracts with a minimal shape

import { TangoClient, ShapeConfig } from "@makegov/tango-node";

const client = new TangoClient({ apiKey: process.env.TANGO_API_KEY });

const contracts = await client.listContracts({
  shape: ShapeConfig.CONTRACTS_MINIMAL,
  keyword: "cloud services",
  awarding_agency: "4700",
  fiscal_year: 2024,
  limit: 10,
});

// Each contract is shaped according to CONTRACTS_MINIMAL
for (const c of contracts.results) {
  console.log(c.piid, c.award_date, c.recipient.display_name);
}

Get a fully-shaped entity

import { TangoClient, ShapeConfig } from "@makegov/tango-node";

const client = new TangoClient({ apiKey: process.env.TANGO_API_KEY });

const entity = await client.getEntity("ABC123DEF456", {
  shape: ShapeConfig.ENTITIES_COMPREHENSIVE,
});

console.log(entity.uei, entity.legal_business_name, entity.primary_naics);

Authentication

The Node SDK uses the same model as the Python one: you can either pass the API key directly or read it from TANGO_API_KEY.

With API key

import { TangoClient } from "@makegov/tango-node";

const client = new TangoClient({
  apiKey: "your-api-key-here",
});

From environment variable (TANGO_API_KEY)

import { TangoClient } from "@makegov/tango-node";

const client = new TangoClient();
// If apiKey is omitted, the client will look for process.env.TANGO_API_KEY

Core Concepts

Dynamic Response Shaping

Response shaping is the core feature of Tango. Instead of always receiving huge objects with every field, you describe the fields you want with a compact shape string:

const contracts = await client.listContracts({
  shape: "key,piid,award_date,recipient(display_name),total_contract_value",
  keyword: "software",
  limit: 5,
});

Shapes:

  • Reduce payload size (often massively).
  • Keep responses focused on what your app actually uses.
  • Drive type safety – the SDK maps the shape to a TypeScript type.

The Node SDK includes:

  • A shape parser that validates shape strings.
  • A schema registry that knows what fields exist on each resource.
  • A type generator and model factory that convert raw API JSON into strongly-typed objects.

Flat vs nested responses

By default, nested fields are returned as nested objects:

// shape:
"key,piid,recipient(display_name,uei)";

//
contract.recipient.display_name;
contract.recipient.uei;

You can request a "flat" representation that uses dotted keys and then unflattens into nested objects on the client:

const contracts = await client.listContracts({
  shape: ShapeConfig.CONTRACTS_MINIMAL,
  flat: true,
});

The Node SDK mirrors the Python client's behavior for shape, flat, and flat_lists.

API Methods

The Node client mirrors the Python SDK's high-level API:

  • listAgencies(options)
  • getAgency(code)
  • listBusinessTypes(options)
  • listContracts(options)
  • listEntities(options)
  • getEntity(ueiOrCage, options)
  • listForecasts(options)
  • listOpportunities(options)
  • listNotices(options)
  • listGrants(options)

All list methods return a paginated response:

interface PaginatedResponse<T> {
  count: number;
  next: string | null;
  previous: string | null;
  pageMetadata: Record<string, unknown> | null;
  results: T[];
}

Error Handling

Errors are surfaced as typed exceptions, aligned with the Python SDK:

  • TangoAPIError – Base error for unexpected issues.
  • TangoAuthError – Authentication problems (e.g., invalid API key, 401).
  • TangoNotFoundError – Resource not found (404).
  • TangoValidationError – Invalid request parameters (400).
  • TangoRateLimitError – Rate limit exceeded (429).

Shape-related errors:

  • ShapeError
  • ShapeValidationError
  • ShapeParseError
  • TypeGenerationError
  • ModelInstantiationError

Use them in your code:

import { TangoClient, TangoAPIError, TangoValidationError } from "@makegov/tango-node";

try {
  const resp = await client.listContracts({ keyword: "cloud", limit: 5 });
} catch (err) {
  if (err instanceof TangoValidationError) {
    console.error("Bad request:", err.message);
  } else if (err instanceof TangoAPIError) {
    console.error("Tango API error:", err.message);
  } else {
    console.error("Unexpected error:", err);
  }
}

Project Structure

tango-node/
├── src/                         # Source TypeScript
│   ├── client.ts                # TangoClient implementation
│   ├── config.ts                # Default base URL + shape presets
│   ├── errors.ts                # Error classes (API, auth, validation, etc.)
│   ├── index.ts                 # Public API exports
│   ├── types.ts                 # Shared types (options, PaginatedResponse)
│   ├── models/                  # Lightweight model interfaces (Contract, Entity, etc.)
│   ├── shapes/                  # Shape system (parser, generator, factory)
│   │   ├── explicitSchemas.ts   # Predefined schemas for resources
│   │   ├── factory.ts           # Instantiate typed models from data
│   │   ├── generator.ts         # Type generation from shape specs
│   │   ├── index.ts             # Shapes exports
│   │   ├── parser.ts            # Shape string parser
│   │   ├── schema.ts            # Schema registry + validation
│   │   ├── schemaTypes.ts       # Schema data structures
│   │   └── types.ts             # Shape spec types
│   └── utils/                   # Helpers
│       ├── dates.ts             # Date/time parsing utilities
│       ├── http.ts              # HTTP client wrapper
│       ├── number.ts            # Numeric parsing/formatting
│       └── unflatten.ts         # Unflatten dotted-key responses
├── docs/                        # Documentation
│   ├── API_REFERENCE.md
│   ├── DYNAMIC_MODELS.md
│   └── SHAPED.md
├── tests/                       # Test suite (Vitest)
│   └── unit/
│       ├── client.test.ts
│       ├── errors.test.ts
│       ├── shapes.factory.test.ts
│       ├── shapes.generator.test.ts
│       ├── shapes.parser.test.ts
│       ├── shapes.schema.test.ts
│       ├── utils.dates.test.ts
│       ├── utils.http.test.ts
│       ├── utils.number.test.ts
│       └── utils.unflatten.test.ts
├── dist/                        # Build output (compiled JS + d.ts) from `npm run build`
├── eslint.config.js             # ESLint flat config
├── .prettierrc                  # Prettier config
├── package.json                 # Package metadata/scripts
├── tsconfig.json                # TypeScript config
├── README.md                    # Usage docs
├── CHANGELOG.md                 # Version history
└── LICENSE                      # MIT license

Development

After cloning the repo:

npm install
npm run build
npm test

Useful scripts:

  • npm run build – Compile TypeScript to dist/.
  • npm test – Run unit and integration tests.
  • npm run coverage – Get test coverage report.
  • npm run lint – Run ESLint.
  • npm run format – Run Prettier.
  • npm run typecheck – TS type checking without emit.

Requirements

Documentation

License

MIT License - see LICENSE for details.

Support

For questions, issues, or feature requests:

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Run tests (npm run test)
  4. Commit your changes (git commit -m 'Add amazing feature')
  5. Push to the branch (git push origin feature/amazing-feature)
  6. Open a Pull Request