npm.io
0.6.7 • Published yesterday

@apicity/dropbox

Licence
MIT
Version
0.6.7
Deps
1
Size
87 kB
Vulns
0
Weekly
0
Stars
1

apicity

CI License: MIT TypeScript Node

A thin wrapper for many APIs: AI image and video generation, all major social media APIs, and more.

Features

  • OTP pay gate — no bypass. Paid endpoints (media generation, etc.) fire only with a human- or code-client-minted, single-use OTP bound to the exact request. An autonomous agent can't self-approve or run up your bill. Details ↓
  • Pre-flight cost estimates. Pure local USD estimates for the billed providers (openai, anthropic, xai, kimicoding, fireworks, alibaba, kie, elevenlabs) — no keys, no network.
  • Schemas for agents. Every POST endpoint ships a zod request schema (endpoint.schema) — hosts and agents catch a hallucinated call locally instead of at the API; the MCP server uses it as the tool's input schema. Provider packages may carry Zod 3 or Zod 4 at runtime; the compatibility policy is documented in docs/zod-compatibility.md.
  • MCP server. Every endpoint exposed 1:1 as an MCP tool.
  • Composable middleware. withRetry / withFallback / withRateLimit as plain function wrappers.
  • Minimal provider dependencies. Providers depend only on zod — plus viem in @apicity/polymarket for order signing. Paid endpoint OTP verification and B2's S3-compatible transport are bundled inside their provider packages. ESM, strict TypeScript.

Example

The headline behavior: a paid endpoint is gated. Without an approved, single-use OTP the call fails closed — an autonomous caller cannot bypass it.

import { createKie } from "@apicity/kie";
import { mintOtp, createCost } from "@apicity/cost";

// The code client holds the pay-gate secret (from your secret manager /
// config). The autonomous caller never sees it, so it can't self-approve.
const secret = loadSecret();
const kie = createKie({
  apiKey: process.env.KIE_API_KEY!,
  paygate: { secret },
});

// Same JSON body you'd POST to /api/v1/jobs/createTask.
const payload = {
  model: "gpt-image-2-text-to-image",
  input: {
    prompt: "A cinematic night-city poster with neon reflections.",
    aspect_ratio: "16:9",
    resolution: "4K",
  },
};

// Pure local cost preview — no keys, no network, sync.
const estimate = createCost().estimate({ provider: "kie", payload });
// estimate.usd === 0.08

// No OTP → fails closed. No bypass.
await kie.post.api.v1.jobs.createTask(payload);
// ❌ throws PayGateError { code: "otp-missing" }

// A human (or the code client) mints a single-use OTP bound to THIS request.
const otp = mintOtp(secret, {
  dotPath: "api.v1.jobs.createTask",
  request: payload,
  ttl: "10m",
});

// Approved — runs once. Replaying the OTP, or changing any byte of the
// payload, fails verification.
const task = await kie.post.api.v1.jobs.createTask(payload, { otp });

Direct KIE VEO calls are gated separately from createTask: for kie.veo.post.api.v1.veo.generate mint with dotPath: "api.v1.veo.generate", for kie.veo.post.api.v1.veo.extend use dotPath: "api.v1.veo.extend". Upload, status, and helper endpoints are unlisted and remain free.

Packages

Package Focus
@apicity/openai OpenAI chat, responses, images, audio, embeddings, files
@apicity/anthropic Anthropic messages, streams, batches, files, models, admin APIs
@apicity/xai xAI chat, responses, Grok images/video, files, collections, search
@apicity/fal fal model registry, generation, pricing, usage, analytics
@apicity/google Google Gemini express-mode generateContent
@apicity/kie KIE media generation for video, image, audio, Claude, Suno
@apicity/alibaba Alibaba DashScope/Qwen chat, image, and video workflows
@apicity/binance Binance Spot REST public/general endpoints
@apicity/openligadb OpenLigaDB public soccer match data, standings, and scorers
@apicity/fireworks Fireworks chat, embeddings, audio, deployments, fine-tuning
@apicity/kimicoding Kimi Coding messages, streaming, models, embeddings
@apicity/elevenlabs ElevenLabs text-to-speech, sound effects, audio APIs
@apicity/s3 S3-compatible object storage
@apicity/b2 Backblaze B2 S3-compatible object storage
@apicity/free-media-upload Public file upload/hosting services
@apicity/x X API posting and media upload
@apicity/meta Instagram Graph API reel publishing
@apicity/polymarket Polymarket Gamma, Data, and CLOB public market data
@apicity/simplefunctions SimpleFunctions public prediction-market and analytical APIs
@apicity/telegram Telegram Bot API text, photo, video, and audio sending
@apicity/thesportsdb TheSportsDB public sports player data
@apicity/cost Pure local cost/token estimates across providers
@apicity/mcp-server MCP server exposing provider endpoints as tools

MCP server

Every endpoint as an MCP tool. Install with a 1Password vault holding the provider keys, or a plain .env file:

claude mcp add apicity -- \
  npx -y @apicity/mcp-server@latest \
  --op-vault apicity --op-token "$OP_SERVICE_ACCOUNT_TOKEN"

# or
claude mcp add apicity -- \
  npx -y @apicity/mcp-server@latest --env-file ~/.config/apicity/.env

Codex: same command after codex mcp add apicity --. Details in @apicity/mcp-server.

Middleware

Every endpoint is a plain (req, signal?) => Promise<T> function, and every package exports withRetry, withFallback, and withRateLimit — generic wrappers that compose, or you can pass endpoint functions into your own orchestration layer. (@apicity/kimicoding also ships withStreamRetry / withStreamFallback for streamed async iterables.)

withRetry — exponential backoff

Retries transient errors (HTTP 429 and 5xx) with configurable backoff:

import { createOpenAi, withRetry } from "@apicity/openai";

const openai = createOpenAi({ apiKey: process.env.OPENAI_API_KEY! });

const chat = withRetry(openai.v1.chat.completions, {
  retries: 3, // max attempts (default: 2)
  baseMs: 500, // initial delay in ms (default: 300)
  factor: 2, // exponential multiplier (default: 2)
  jitter: true, // randomize delay ±20% (default: true)
});
withFallback — multi-provider failover

Tries each function in order; wrappers keep the same signature, so they nest:

import { createXai, withFallback, withRetry } from "@apicity/xai";

const primary = createXai({ apiKey: process.env.XAI_API_KEY_PRIMARY! });
const backup = createXai({ apiKey: process.env.XAI_API_KEY_BACKUP! });

const image = withFallback([
  withRetry(primary.v1.images.generations, { retries: 2 }),
  withRetry(backup.v1.images.generations, { retries: 1 }),
]);

const result = await image({
  model: "grok-2-image",
  prompt: "A product photo of a small brass desk lamp",
  n: 1,
});
withRateLimit — client-side throttling

Bounds requests-per-minute and concurrency through a shared limiter:

import {
  createOpenAi,
  withRateLimit,
  createRateLimiter,
} from "@apicity/openai";

const openai = createOpenAi({ apiKey: process.env.OPENAI_API_KEY! });
const limiter = createRateLimiter({ rpm: 60, concurrent: 5 });

const chat = withRateLimit(openai.v1.chat.completions, limiter);

Paid endpoints (OTP pay gate)

Endpoints with direct marginal cost (e.g. kie.post.api.v1.jobs.createTask and direct VEO calls under kie.veo.post.api.v1.veo.*) are listed in PAID_ENDPOINTS and gated behind a single-use OTP — the flow is the example above. The gate is fail-closed: a paid call needs both a pay-gate secret at provider construction and a valid OTP minted from that same secret. The autonomous caller never sees the secret, so it cannot self-approve. Unlisted endpoints are free.

The OTP is signed with a single shared HMAC secret — no key files, no environment variables, no cost coupling — and commits to the exact (provider, method, dotPath, requestHash, exp) tuple: change any byte of the payload and verification fails. The jti is consumed before dispatch, so a failed network call still burns the token — mint a fresh OTP for any retry.

Operators (or the code client) mint OTPs with mintOtp(secret, { dotPath, request, ttl }) or the CLI — the secret is read from a file, never an env var:

apicity-paygate otp mint \
  --secret-file ./paygate.secret \
  --dot-path api.v1.jobs.createTask \
  --payload-file request.json \
  --ttl 10m

A blocked call throws PayGateError whose .code is one of paygate-not-configured, otp-missing, otp-malformed, otp-invalid-signature, otp-expired, otp-mismatched-request, or otp-replayed.

The gate is generic — xai and others opt in by adding a PAID_ENDPOINTS entry. See @apicity/cost for the full spec and the MCP server's --paygate-secret-file wiring.

Development

  • Runtime — Node 18+, Cloudflare Workers, Deno, Bun. ESM only.
  • Build & testpnpm install && pnpm run build && pnpm run test:run. Integration tests record/replay via Polly.js (no keys needed for replay).
  • Provider-scoped loop — use pnpm run dev:preflight:fast -- <name-or-path> as the fast pre-push checklist for narrow provider work. It prints and runs the scoped steps: Prettier on the provider package/tests, lint:provider, then test:provider for provider typecheck + replay tests. <name-or-path> can be openai, packages/provider/openai/src/openai.ts, or a matching tests/integration/openai-*.test.ts path. From a provider package, pnpm -w run dev:preflight:fast infers the provider from INIT_CWD. Use pnpm run test:provider -- <name-or-path> for test-only loops, pnpm run typecheck:provider -- <name-or-path> for provider-only compile checks, and pnpm run lint:provider -- <name-or-path> for provider-only lint. pnpm run test:affected auto-selects provider tests from the current git diff when every changed file belongs to provider packages, provider integration tests, or provider recordings; it falls back to full pnpm run test:run for shared scripts/config, unit or functional tests, docs, package metadata, or other ambiguous changes. pnpm run dev:preflight and pnpm run ci:local remain the full repository gates.
  • Changed-file loop — use pnpm run format:changed to run Prettier only on files changed from origin/main, staged/unstaged files, and untracked files. Use pnpm run dev:preflight:changed when you want that scoped Prettier step followed by the repository checks that still run globally: full typecheck, scoped ESLint for changed JS/TS files, endpoint comment checks, orphan-recording checks, test-timer checks, and the replay suite. Pass explicit paths when the changed surface is known; for example, pass package.json README.md after --. Prefer dev:preflight:fast instead when the diff is cleanly provider-scoped. The full repository gates remain pnpm run lint for format check plus full linting/repo checks, pnpm run lint:after-format after formatting, pnpm run dev:preflight, and pnpm run ci:local.
  • Typecheck-only fast path — use pnpm run typecheck:provider -- <name-or-path> for provider-only diffs. It includes staged and unstaged files in its safety check, and falls back to the full pnpm run typecheck when the diff touches another package or shared package/TypeScript config. Baseline timing on 2026-06-30: full typecheck 105.61s real, representative provider tsconfig 5.79s real; the guarded fast path validated at 10.33s real with pnpm run typecheck:provider -- openai --base=HEAD.
  • Validate before sending — every POST endpoint exposes a .schema: createOpenAi(...).v1.chat.completions.schema.safeParse(payload) catches a hallucinated call locally instead of at the API.

License

MIT — see LICENSE.

Based on TetherAI by Nenad Bursac.

Keywords