How to Use Heroku API: Complete Integration Guide (2026)

Master Heroku API integration with this complete guide. Learn OAuth authentication, app management, dyno scaling, builds, pipelines, and production best practices.

Ashley Innocent

Ashley Innocent

25 March 2026

How to Use Heroku API: Complete Integration Guide (2026)

TL;DR

The Heroku API enables developers to automate deployment, manage applications, configure add-ons, and scale infrastructure programmatically. It uses OAuth 2.0 and token-based authentication, RESTful endpoints for apps, dynos, builds, and pipelines, with rate limits of 10,000 requests per hour per account. This guide covers authentication setup, core endpoints, CI/CD integration, and production deployment strategies.

Introduction

Heroku powers over 4 million applications across 170+ countries. For developers building deployment automation, CI/CD pipelines, or multi-app management tools, Heroku API integration isn’t optional—it’s essential.

Here’s the reality: teams managing 10+ Heroku apps lose 8-12 hours weekly on manual deploys and configuration changes. A solid Heroku API integration automates deployments, scales dynos based on traffic, and synchronizes configuration across environments.

This guide walks through the complete Heroku API integration process. You’ll learn token authentication, app and dyno management, build pipelines, add-on provisioning, and error troubleshooting. By the end, you’ll have a production-ready Heroku integration.

💡
Apidog simplifies API integration testing. Test your Heroku endpoints, validate authentication flows, inspect API responses, and debug configuration issues in one workspace. Import API specifications, mock responses, and share test scenarios with your team.

What Is the Heroku API?

Heroku provides a RESTful Platform API for managing applications and infrastructure on the Heroku platform. The API handles:

Key Features

Feature Description
RESTful Design Standard HTTP methods with JSON responses
Token Authentication Bearer token with OAuth 2.0 support
Range Requests Pagination for large result sets
Rate Limiting 10,000 requests per hour per account
Idempotent Creates Safe retry behavior for writes
Gzip Compression Response compression for bandwidth savings

API Architecture Overview

Heroku uses a versioned REST API structure:

https://api.heroku.com/

The API follows JSON:API specification with consistent resource patterns and relationships.

API Versions Compared

Version Status Authentication Use Case
Platform API (v3) Current Bearer Token All new integrations
GitHub Integration Current OAuth 2.0 GitHub-connected apps
Container Registry Current Docker auth Container deployments

Getting Started: Authentication Setup

Step 1: Create Your Heroku Account

Before accessing the API, you need a Heroku account:

  1. Visit the Heroku website
  2. Click Sign Up and create an account
  3. Verify your email address
  4. Complete account setup

Step 2: Install Heroku CLI

The CLI helps generate API tokens and test commands:

# macOS
brew tap heroku/brew && brew install heroku

# Windows
npm install -g heroku

# Linux
curl https://cli-assets.heroku.com/install.sh | sh

Step 3: Generate API Token

Authenticate with Heroku CLI:

# Login to Heroku
heroku login

# This opens a browser for authentication
# After login, your token is stored locally

Retrieve your API token:

# View your current authorization token
heroku authorizations:create --short-lived

# Or create a long-lived token (for CI/CD)
heroku authorizations:create --description "CI/CD Pipeline" --expires-in "1 year"

Security note: Store tokens in environment variables, never in code:

# .env file
HEROKU_API_KEY="your_api_key_here"
HEROKU_APP_NAME="your-app-name"

Step 4: Understand Token Authentication

Heroku uses Bearer token authentication:

Authorization: Bearer {api_key}
Accept: application/vnd.heroku+json; version=3

Every API request requires these headers.

Step 5: Make Your First API Call

Test your authentication:

curl -n https://api.heroku.com/account \
  -H "Accept: application/vnd.heroku+json; version=3" \
  -H "Authorization: Bearer $HEROKU_API_KEY"

Expected response:

{
  "id": "user-id-here",
  "email": "developer@example.com",
  "name": "Developer Name",
  "created_at": "2024-01-15T10:30:00Z",
  "updated_at": "2026-03-20T14:22:00Z"
}

Step 6: Implement Authentication in Code

Create a reusable API client:

const HEROKU_API_KEY = process.env.HEROKU_API_KEY;
const HEROKU_BASE_URL = 'https://api.heroku.com';

const herokuRequest = async (endpoint, options = {}) => {
  const response = await fetch(`${HEROKU_BASE_URL}${endpoint}`, {
    ...options,
    headers: {
      'Authorization': `Bearer ${HEROKU_API_KEY}`,
      'Accept': 'application/vnd.heroku+json; version=3',
      'Content-Type': 'application/json',
      ...options.headers
    }
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Heroku API Error: ${error.message}`);
  }

  return response.json();
};

// Usage
const account = await herokuRequest('/account');
console.log(`Logged in as: ${account.email}`);

Application Management

Creating a New App

Create a Heroku app programmatically:

const createApp = async (appName, region = 'us') => {
  const response = await herokuRequest('/apps', {
    method: 'POST',
    body: JSON.stringify({
      name: appName,
      region: region
    })
  });

  return response;
};

// Usage
const app = await createApp('my-awesome-app-2026');
console.log(`App created: ${app.name}`);
console.log(`Git URL: ${app.git_url}`);
console.log(`Web URL: ${app.web_url}`);

Expected App Response

{
  "id": "app-uuid-here",
  "name": "my-awesome-app-2026",
  "region": { "name": "us" },
  "created_at": "2026-03-25T10:00:00Z",
  "updated_at": "2026-03-25T10:00:00Z",
  "git_url": "https://git.heroku.com/my-awesome-app-2026.git",
  "web_url": "https://my-awesome-app-2026.herokuapp.com",
  "owner": { "email": "developer@example.com" },
  "build_stack": { "name": "heroku-24" }
}

Listing Your Apps

Fetch all apps in your account:

const listApps = async (limit = 50) => {
  const response = await herokuRequest(`/apps?limit=${limit}`);
  return response;
};

// Usage
const apps = await listApps();
apps.forEach(app => {
  console.log(`${app.name} - ${app.web_url}`);
});

Getting App Details

Retrieve detailed app information:

const getApp = async (appName) => {
  const response = await herokuRequest(`/apps/${appName}`);
  return response;
};

// Usage
const app = await getApp('my-awesome-app-2026');
console.log(`Stack: ${app.build_stack.name}`);
console.log(`Region: ${app.region.name}`);

Updating App Configuration

Modify app settings:

const updateApp = async (appName, updates) => {
  const response = await herokuRequest(`/apps/${appName}`, {
    method: 'PATCH',
    body: JSON.stringify(updates)
  });

  return response;
};

// Usage - change app name
const updated = await updateApp('old-app-name', {
  name: 'new-app-name'
});

Deleting an App

Remove an app from your account:

const deleteApp = async (appName) => {
  await herokuRequest(`/apps/${appName}`, {
    method: 'DELETE'
  });

  console.log(`App ${appName} deleted successfully`);
};

Dyno Management

Scaling Dynos

Scale your application up or down:

const scaleDyno = async (appName, processType, quantity) => {
  const response = await herokuRequest(`/apps/${appName}/formation/${processType}`, {
    method: 'PATCH',
    body: JSON.stringify({
      quantity: quantity
    })
  });

  return response;
};

// Usage - scale web dynos to 3
const formation = await scaleDyno('my-app', 'web', 3);
console.log(`Scaled to ${formation.quantity} ${processType} dynos`);

Getting Dyno Formation

View current dyno configuration:

const getFormation = async (appName, processType = null) => {
  const endpoint = processType
    ? `/apps/${appName}/formation/${processType}`
    : `/apps/${appName}/formation`;

  const response = await herokuRequest(endpoint);
  return response;
};

// Usage
const formation = await getFormation('my-app');
formation.forEach(proc => {
  console.log(`${proc.type}: ${proc.quantity} dynos (@ ${proc.size})`);
});

Available Dyno Sizes

Dyno Type Use Case Cost/Month
eco Hobby projects, demos $5
basic Small production apps $7
standard-1x Standard workloads $25
standard-2x High-performance apps $50
performance Production-critical apps $250+
private Enterprise isolation Custom

Restarting Dynos

Restart all dynos for an app:

const restartDynos = async (appName, processType = null) => {
  const endpoint = processType
    ? `/apps/${appName}/formation/${processType}`
    : `/apps/${appName}/dynos`;

  await herokuRequest(endpoint, {
    method: 'DELETE'
  });

  console.log(`Dynos restarted for ${appName}`);
};

Running One-Off Dynos

Execute commands in isolated dynos:

const runCommand = async (appName, command) => {
  const response = await herokuRequest(`/apps/${appName}/dynos`, {
    method: 'POST',
    body: JSON.stringify({
      command: command,
      size: 'standard-1x'
    })
  });

  return response;
};

// Usage - run database migration
const dyno = await runCommand('my-app', 'npm run migrate');
console.log(`Dyno started: ${dyno.id}`);

Configuration Variables

Getting Config Vars

Retrieve environment variables:

const getConfigVars = async (appName) => {
  const response = await herokuRequest(`/apps/${appName}/config-vars`);
  return response;
};

// Usage
const config = await getConfigVars('my-app');
console.log(`DATABASE_URL: ${config.DATABASE_URL}`);
console.log(`NODE_ENV: ${config.NODE_ENV}`);

Setting Config Vars

Update environment variables:

const setConfigVars = async (appName, variables) => {
  const response = await herokuRequest(`/apps/${appName}/config-vars`, {
    method: 'PATCH',
    body: JSON.stringify(variables)
  });

  return response;
};

// Usage
const updated = await setConfigVars('my-app', {
  NODE_ENV: 'production',
  API_SECRET: 'your-secret-key',
  LOG_LEVEL: 'info'
});

Best Practices for Config Vars

  1. Never commit secrets - Use environment variables for all sensitive data
  2. Use separate configs per environment - Different vars for staging vs production
  3. Rotate secrets regularly - Update API keys and passwords quarterly
  4. Prefix related variables - STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET

Build and Release Management

Creating a Build

Deploy code via the API:

const createBuild = async (appName, sourceBlobUrl) => {
  const response = await herokuRequest(`/apps/${appName}/builds`, {
    method: 'POST',
    body: JSON.stringify({
      source_blob: {
        url: sourceBlobUrl
      }
    })
  });

  return response;
};

// Usage
const build = await createBuild('my-app', 'https://storage.example.com/source.tar.gz');
console.log(`Build started: ${build.id}`);
console.log(`Status: ${build.status}`);

Getting Build Status

Check build progress:

const getBuild = async (appName, buildId) => {
  const response = await herokuRequest(`/apps/${appName}/builds/${buildId}`);
  return response;
};

// Poll until complete
const checkBuildStatus = async (appName, buildId, maxAttempts = 30) => {
  for (let i = 0; i < maxAttempts; i++) {
    const build = await getBuild(appName, buildId);

    if (build.status === 'succeeded') {
      console.log('Build succeeded!');
      return build;
    } else if (build.status === 'failed') {
      throw new Error(`Build failed: ${build.output}`);
    }

    console.log(`Build in progress... attempt ${i + 1}`);
    await new Promise(resolve => setTimeout(resolve, 5000));
  }

  throw new Error('Build timeout');
};

Listing Releases

View release history:

const listReleases = async (appName, limit = 10) => {
  const response = await herokuRequest(`/apps/${appName}/releases?limit=${limit}`);
  return response;
};

// Usage
const releases = await listReleases('my-app');
releases.forEach(release => {
  console.log(`v${release.version} - ${release.description} - ${release.created_at}`);
});

Rolling Back to Previous Release

Revert to a previous version:

const rollback = async (appName, releaseId) => {
  const response = await herokuRequest(`/apps/${appName}/releases`, {
    method: 'POST',
    body: JSON.stringify({
      rollback: releaseId
    })
  });

  return response;
};

// Usage - rollback to version 42
const rollbackRelease = await rollback('my-app', 42);
console.log(`Rolled back to v${rollbackRelease.version}`);

Pipeline Management

Creating a Pipeline

Set up CI/CD pipelines:

const createPipeline = async (pipelineName) => {
  const response = await herokuRequest('/pipelines', {
    method: 'POST',
    body: JSON.stringify({
      name: pipelineName
    })
  });

  return response;
};

// Usage
const pipeline = await createPipeline('my-app-pipeline');
console.log(`Pipeline created: ${pipeline.id}`);

Adding Apps to Pipeline

Connect apps to pipeline stages:

const addAppToPipeline = async (pipelineId, appName, stage) => {
  const response = await herokuRequest('/pipeline-couplings', {
    method: 'POST',
    body: JSON.stringify({
      pipeline: pipelineId,
      app: appName,
      stage: stage // 'development', 'staging', 'production'
    })
  });

  return response;
};

// Usage
await addAppToPipeline(pipelineId, 'my-app-dev', 'development');
await addAppToPipeline(pipelineId, 'my-app-staging', 'staging');
await addAppToPipeline(pipelineId, 'my-app-prod', 'production');

Promoting Slug to Next Stage

Move code through pipeline:

const promoteSlug = async (slugId, toApp) => {
  await herokuRequest('/promotions', {
    method: 'POST',
    body: JSON.stringify({
      from: toApp, // Source app
      to: toApp,   // Target app (next stage)
      slug: slugId
    })
  });

  console.log(`Promoted slug ${slugId} to ${toApp}`);
};

Add-On Management

Provisioning Add-Ons

Install Heroku add-ons:

const provisionAddon = async (appName, addonPlan, config = {}) => {
  const response = await herokuRequest('/addon-attachments', {
    method: 'POST',
    body: JSON.stringify({
      app: appName,
      plan: addonPlan,
      config: config
    })
  });

  return response;
};

// Usage - provision PostgreSQL
const db = await provisionAddon('my-app', 'heroku-postgresql:mini', {});
console.log(`Database provisioned: ${db.addon.name}`);
console.log(`DATABASE_URL: ${db.addon.config_vars.DATABASE_URL}`);
Add-On Plan Starting Price Use Case
heroku-postgresql mini $5/mo Production database
heroku-redis mini $5/mo Caching, sessions
papertrail choklad $7/mo Log aggregation
sentry developer Free Error tracking
mailgun sandbox Free Email delivery
newrelic lite Free Application monitoring

Listing Add-Ons

View installed add-ons:

const listAddons = async (appName) => {
  const response = await herokuRequest(`/apps/${appName}/addons`);
  return response;
};

// Usage
const addons = await listAddons('my-app');
addons.forEach(addon => {
  console.log(`${addon.plan.name} - $${addon.pricing.plan.price} - ${addon.state}`);
});

Removing Add-Ons

Uninstall add-ons:

const removeAddon = async (appName, addonId) => {
  await herokuRequest(`/apps/${appName}/addons/${addonId}`, {
    method: 'DELETE'
  });

  console.log(`Addon ${addonId} removed from ${appName}`);
};

Domain and SSL Management

Adding Custom Domains

Configure custom domains:

const addDomain = async (appName, domainName) => {
  const response = await herokuRequest(`/apps/${appName}/domains`, {
    method: 'POST',
    body: JSON.stringify({
      hostname: domainName
    })
  });

  return response;
};

// Usage
const domain = await addDomain('my-app', 'api.example.com');
console.log(`CNAME target: ${domain.cname}`);

Configuring SSL Certificates

Add SSL to custom domains:

const addSslCertificate = async (appName, domainId, certificateChain, privateKey) => {
  const response = await herokuRequest(`/apps/${appName}/domains/${domainId}/ssl_endpoint`, {
    method: 'PATCH',
    body: JSON.stringify({
      ssl_cert: {
        cert_chain: certificateChain,
        private_key: privateKey
      }
    })
  });

  return response;
};

Automated SSL with ACM

Enable Automatic Certificate Management:

const enableACM = async (appName, domainName) => {
  const response = await herokuRequest(`/apps/${appName}/domains/${domainName}/sni_endpoint`, {
    method: 'POST',
    body: JSON.stringify({
      kind: 'acm'
    })
  });

  return response;
};

Rate Limiting and Quotas

Understanding Rate Limits

Heroku enforces rate limits to protect API stability:

Exceeding limits results in HTTP 429 (Too Many Requests) responses.

Implementing Rate Limit Handling

Use exponential backoff for retries:

const makeRateLimitedRequest = async (endpoint, options = {}, maxRetries = 3) => {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await herokuRequest(endpoint, options);

      // Check rate limit headers
      const remaining = response.headers.get('RateLimit-Remaining');
      const resetTime = response.headers.get('RateLimit-Reset');

      if (remaining < 100) {
        console.warn(`Low quota remaining: ${remaining}, resets at ${resetTime}`);
      }

      return response;
    } catch (error) {
      if (error.message.includes('429') && attempt < maxRetries) {
        const delay = Math.pow(2, attempt) * 1000;
        console.log(`Rate limited. Retrying in ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
};

Rate Limit Headers

Heroku includes these headers in every response:

Header Description
RateLimit-Limit Maximum requests per hour
RateLimit-Remaining Remaining requests in window
RateLimit-Reset Unix timestamp when window resets

Troubleshooting Common Issues

Issue: Authentication Fails with 401

Symptoms: Getting “Invalid credentials” errors.

Solutions:

  1. Verify API key is correct: heroku authorizations
  2. Ensure token hasn’t expired (long-lived tokens last 1 year)
  3. Check for extra whitespace in environment variable
  4. Regenerate token if needed: heroku authorizations:create

Issue: App Name Already Taken

Symptoms: Getting “name is already taken” error.

Solutions:

  1. Use globally unique names - include team or random suffix
  2. Generate UUID-based names: app-${Date.now()}
  3. Use namespace prefixes: teamname-appname-env
const generateUniqueAppName = (baseName) => {
  const timestamp = Date.now().toString(36);
  const random = Math.random().toString(36).substring(2, 6);
  return `${baseName}-${timestamp}-${random}`;
};

Issue: Dyno Formation Fails

Symptoms: Scaling operations return errors.

Solutions:

  1. Verify process type exists in Procfile
  2. Check account has available dyno quota
  3. Ensure app is not suspended
  4. Review dyno hour usage: heroku ps --app=my-app

Issue: Build Fails with Timeout

Symptoms: Builds hang or timeout after 30 minutes.

Solutions:

  1. Optimize buildpack selection for your language
  2. Cache dependencies properly
  3. Split large builds into smaller deployments
  4. Use pre-built slugs for faster deployment

Issue: Rate Limit Exceeded

Symptoms: Receiving HTTP 429 responses.

Solutions:

  1. Implement request queuing
  2. Use exponential backoff for retries
  3. Batch requests where possible
  4. Monitor rate limit headers proactively
// Simple rate limiter
class HerokuRateLimiter {
  constructor(requestsPerMinute = 150) {
    this.queue = [];
    this.interval = 60000 / requestsPerMinute;
    this.processing = false;
  }

  async add(requestFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ requestFn, resolve, reject });
      this.process();
    });
  }

  async process() {
    if (this.processing || this.queue.length === 0) return;

    this.processing = true;

    while (this.queue.length > 0) {
      const { requestFn, resolve, reject } = this.queue.shift();

      try {
        const result = await requestFn();
        resolve(result);
      } catch (error) {
        reject(error);
      }

      if (this.queue.length > 0) {
        await new Promise(r => setTimeout(r, this.interval));
      }
    }

    this.processing = false;
  }
}

Production Deployment Checklist

Before going live:

Monitoring and Alerting

Track these metrics:

const metrics = {
  apiCalls: {
    total: 0,
    successful: 0,
    failed: 0,
    rateLimited: 0
  },
  dynoHours: {
    used: 0,
    quota: 1000
  },
  deployments: {
    successful: 0,
    failed: 0,
    avg_duration: 0
  }
};

// Alert on high failure rate
const failureRate = metrics.apiCalls.failed / metrics.apiCalls.total;

if (failureRate > 0.05) {
  sendAlert('Heroku API failure rate above 5%');
}

// Alert on dyno hour usage
if (metrics.dynoHours.used > metrics.dynoHours.quota * 0.8) {
  sendAlert('Dyno hour usage above 80%');
}

Real-World Use Cases

Automated CI/CD Pipeline

A SaaS team automates deployments from GitHub:

Implementation flow:

  1. GitHub push triggers workflow
  2. Tests run in CI
  3. Heroku API creates build from source blob
  4. Promote through staging to production
  5. Notify team on success/failure

Multi-Environment Management

A consulting firm manages 50+ client apps:

Key integration points:

Auto-Scaling Based on Traffic

An e-commerce platform handles traffic spikes:

Auto-scaling logic:

Conclusion

The Heroku API provides comprehensive access to platform functionality. Key takeaways:

button

FAQ Section

What is the Heroku API used for?

The Heroku API enables programmatic management of applications, dynos, add-ons, and infrastructure. Common use cases include CI/CD automation, multi-app management tools, auto-scaling systems, and infrastructure monitoring dashboards.

How do I get a Heroku API key?

Install the Heroku CLI, run heroku login, then create an authorization with heroku authorizations:create. Store the returned token securely in environment variables.

Is the Heroku API free to use?

Yes, the Heroku API is free. However, you pay for the resources you provision (dynos, add-ons, etc.). API rate limits are 10,000 requests per hour per account.

What authentication does Heroku API use?

Heroku uses Bearer token authentication. Include Authorization: Bearer {api_key} header in every request. Tokens can be short-lived (1 hour) or long-lived (up to 1 year).

How do I handle Heroku API rate limits?

Monitor the RateLimit-Remaining header and implement request queuing. Use exponential backoff when receiving HTTP 429 responses. Stay under 150 requests per minute for safe operation.

Can I deploy without Git?

Yes. Use the Builds API to deploy from a source blob URL. Upload your code to cloud storage (S3, GCS) and reference the URL in your build request.

How do I automate deployments?

Use the Pipeline API to set up CI/CD. Create builds, promote slugs through stages, and integrate with GitHub or custom CI systems.

What is the difference between a release and a build?

A build compiles your source code into a slug. A release combines a slug with configuration variables to create a deployable version of your app.

How do I rollback a failed deployment?

Use the Releases API to list recent releases, then POST to /releases with rollback: <release_id>. Heroku creates a new release at the previous version.

Can I manage multiple Heroku accounts?

Yes. Use separate API tokens for each account and switch between them by changing the HEROKU_API_KEY environment variable.

Explore more

How to Manage Internal vs External API: Complete Guide

How to Manage Internal vs External API: Complete Guide

Discover how to manage internal vs external API with proven strategies, practical examples, and tips for seamless, secure API lifecycle management.

25 March 2026

Google API Gateway: Complete Guide, Features, & Best Practices

Google API Gateway: Complete Guide, Features, & Best Practices

Learn everything about Google API Gateway: what it is, how it enables secure and scalable API management, setup steps, pricing, and real-world examples. Discover best practices and how Apidog can streamline your API workflow with Google API Gateway.

25 March 2026

API Discovery: What It Is & How to Master It

API Discovery: What It Is & How to Master It

API Discovery is the process of identifying, cataloging, and managing every API endpoint in your organization. Learn why API Discovery is crucial for security, compliance, and innovation—and how tools like Apidog can streamline the process.

25 March 2026

Practice API Design-first in Apidog

Discover an easier way to build and use APIs