---
title: "Programmatic API"
description: "Local drop-in replacement services for CI and no-network sandboxes. Fully stateful, production-fidelity API emulation."
canonical_url: "https://emulate.dev/docs/programmatic-api"
---
# Programmatic API

Use emulators as libraries in your tests and scripts. There are two approaches: the `emulate` CLI package (which wraps all services) and the individual `@emulators/*` scoped packages.

## CLI package

The `emulate` package provides a `createEmulator` helper that starts any service by name.

```bash
npm install emulate
```

```typescript
import { createEmulator } from 'emulate'

const github = await createEmulator({ service: 'github', port: 4001 })
const vercel = await createEmulator({ service: 'vercel', port: 4002 })

github.url   // 'http://localhost:4001'
vercel.url   // 'http://localhost:4002'

await github.close()
await vercel.close()
```

### Generated secrets

When a GitHub App omits `private_key`, `createEmulator` generates an RSA-2048 PKCS#1 key for that emulator instance:

```typescript
const github = await createEmulator({
  service: 'github',
  seed: {
    github: {
      users: [{ login: 'octocat' }],
      apps: [{
        app_id: 12345,
        slug: 'my-github-app',
        name: 'My GitHub App',
        installations: [{ installation_id: 100, account: 'octocat' }],
      }],
    },
  },
})

const privateKey = github.generatedSecrets.find(
  secret => secret.kind === 'github.app_private_key' && secret.id === '12345',
)?.value
```

Generated keys remain stable across `reset()` calls and appear only in `generatedSecrets`. Explicitly configured keys are never returned there. A new `createEmulator` call generates a new key.

### Vitest / Jest setup

```typescript
// vitest.setup.ts
import { createEmulator, type Emulator } from 'emulate'

let github: Emulator
let vercel: Emulator

beforeAll(async () => {
  ;[github, vercel] = await Promise.all([
    createEmulator({ service: 'github', port: 4001 }),
    createEmulator({ service: 'vercel', port: 4002 }),
  ])
  process.env.GITHUB_EMULATOR_URL = github.url
  process.env.VERCEL_EMULATOR_URL = vercel.url
})

afterEach(() => { github.reset(); vercel.reset() })
afterAll(() => Promise.all([github.close(), vercel.close()]))
```

### createEmulator options

<table>
  <thead>
    <tr>
      <th>Option</th>
      <th>Default</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code>service</code></td>
      <td><em>(required)</em></td>
      <td>Service name: <code>'vercel'</code>, <code>'github'</code>, <code>'google'</code>, <code>'slack'</code>, <code>'apple'</code>, <code>'microsoft'</code>, <code>'okta'</code>, <code>'aws'</code>, <code>'resend'</code>, <code>'stripe'</code>, <code>'mongoatlas'</code>, <code>'clerk'</code>, <code>'linear'</code>, or <code>'twilio'</code></td>
    </tr>
    <tr>
      <td><code>port</code></td>
      <td><code>4000</code></td>
      <td>Port for the HTTP server</td>
    </tr>
    <tr>
      <td><code>hostname</code></td>
      <td><code>127.0.0.1</code></td>
      <td>Listening address for built-in and custom emulators</td>
    </tr>
    <tr>
      <td><code>seed</code></td>
      <td>none</td>
      <td>Inline seed data (same shape as YAML config)</td>
    </tr>
    <tr>
      <td><code>baseUrl</code></td>
      <td>none</td>
      <td>Override the advertised base URL (used in OAuth redirects, webhook URLs, etc.). Per-service <code>baseUrl</code> in seed config takes highest priority, then this option, then the <code>EMULATE_BASE_URL</code> env var (supports <code>{'{service}'}</code> template), then <code>PORTLESS_URL</code> (supports <code>{'{service}'}</code>, automatically set by the <code>portless</code> CLI wrapper), then <code>{`http://localhost:<port>`}</code>.</td>
    </tr>
  </tbody>
</table>

Use `hostname: '0.0.0.0'` to allow connections from containers or other machines, and `baseUrl` to advertise a URL reachable by those clients. This option also applies to custom definitions passed to `createEmulator`.

### Instance methods

<table>
  <thead>
    <tr>
      <th>Method</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code>url</code></td>
      <td>Base URL of the running server</td>
    </tr>
    <tr>
      <td><code>generatedSecrets</code></td>
      <td>Readonly secrets generated while preparing seed data</td>
    </tr>
    <tr>
      <td><code>reset()</code></td>
      <td>Wipe the store and replay seed data</td>
    </tr>
    <tr>
      <td><code>close()</code></td>
      <td>Shut down the HTTP server, returns a Promise</td>
    </tr>
  </tbody>
</table>

## Scoped packages

Each emulator is published as its own `@emulators/*` package. Install only the ones you need:

```bash
npm install @emulators/github @emulators/google @emulators/linear @emulators/twilio
```

### Available packages

<table>
  <thead>
    <tr>
      <th>Package</th>
      <th>Service</th>
    </tr>
  </thead>
  <tbody>
    <tr><td><code>@emulators/vercel</code></td><td>Vercel API</td></tr>
    <tr><td><code>@emulators/github</code></td><td>GitHub API</td></tr>
    <tr><td><code>@emulators/google</code></td><td>Google OAuth, Gmail, Calendar, Drive</td></tr>
    <tr><td><code>@emulators/slack</code></td><td>Slack Web API, OAuth v2, webhooks</td></tr>
    <tr><td><code>@emulators/linear</code></td><td>Linear GraphQL API, OAuth, webhooks</td></tr>
    <tr><td><code>@emulators/twilio</code></td><td>Twilio Messaging, Verify, Voice, webhooks</td></tr>
    <tr><td><code>@emulators/apple</code></td><td>Apple Sign In / OIDC</td></tr>
    <tr><td><code>@emulators/microsoft</code></td><td>Microsoft Entra ID, Graph API</td></tr>
    <tr><td><code>@emulators/aws</code></td><td>AWS S3, SQS, IAM, STS</td></tr>
    <tr><td><code>@emulators/okta</code></td><td>Okta identity provider / OIDC</td></tr>
    <tr><td><code>@emulators/mongoatlas</code></td><td>MongoDB Atlas Admin API + Data API</td></tr>
    <tr><td><code>@emulators/resend</code></td><td>Resend email API</td></tr>
    <tr><td><code>@emulators/stripe</code></td><td>Stripe billing and payments API</td></tr>
    <tr><td><code>@emulators/clerk</code></td><td>Clerk Backend API and OAuth</td></tr>
    <tr><td><code>@emulators/core</code></td><td>Shared store, middleware, and utilities</td></tr>
    <tr><td><code>@emulators/adapter-next</code></td><td>Next.js App Router integration</td></tr>
    <tr><td><code>@emulators/adapter-nuxt</code></td><td>Nuxt server route integration</td></tr>
  </tbody>
</table>

### Direct usage

Each scoped package exports its service plugin and seed helpers:

```typescript
import * as github from '@emulators/github'
import * as stripe from '@emulators/stripe'
```

These are the same modules used internally by the CLI and the [Next.js adapter](/docs/nextjs) or [Nuxt adapter](/docs/nuxt). See the framework integration pages for full examples of using scoped packages with `createEmulateHandler`.

## Custom emulators

Custom definitions created with `defineEmulator` from `emulate` work in the CLI, in-process tests, and both framework adapters. The same definition provides typed state, HTTP handlers, reset, versioned snapshots, and optional inspection/persistence. See [Custom emulators](/docs/custom-emulators) for the quickstart, API reference, and complete lifecycle semantics.
