Skip to main content

Podhoc API Documentation

Integrate podcast generation into your applications with the Podhoc REST API. Create, manage, and download AI-generated podcasts programmatically.

Integrate podcast generation into your applications

Podhoc offers a REST API on Pro for URL-based generation. Google Cloud documents a Preview API for notebook Audio Overviews and a standalone podcast API. Their setup, authentication and limits differ; consult the current API documentation before choosing an integration.

Google Cloud audio API. Comparison checked on 16 September 2026.

Publication default: for production tokens, omitting auto_publish is equivalent to true: the generated podcast can be public in Discover. Send "auto_publish": false explicitly to request private output; the account must allow publication control. Test tokens default to false and cannot auto-publish. Private source storage does not make the generated audio private.

Breaking change – style is now validated. An explicitly-supplied style that is not one of the nine canonical ids below now returns 400 INVALID_STYLE. Until this change an unrecognised value was accepted silently and generated as deep_dive. Earlier versions of this page used "style": "conversational" in every example – conversational was never a real style id, so those calls were producing deep_dive all along and the audio you received was not what the docs implied. Switch any unrecognised value to a canonical id. Omitting style, or sending null or an empty string, still defaults to deep_dive, so callers who never set the field are unaffected.

Content Sources: The API accepts publicly accessible URLs only. File uploads and raw text are not currently supported. If your organisation has compliance or security requirements that prevent public hosting, please create a support ticket to discuss enterprise integration options.

Quick start

  1. Get a PRO subscription at app.podhoc.com
  2. Create an API token at app.podhoc.com/account/api-access
  3. Make your first request using the examples below

Authentication

All API requests require an X-Api-Key header with your API token:

X-Api-Key: phk_test_a1b2c3d4e5f6...

Base URL: https://api-ext.podhoc.com/v1

Token types:

  • Test tokens (phk_test_...) – Cheaper, limited features. For development and testing.
  • Production tokens (phk_prod_...) – Expensive, full access. For production use.

Endpoints

POST /v1/podcasts – Create a podcast

Start generating a new podcast from one or more sources.

Request parameters:

ParameterTypeRequiredDefaultDescription
urlsstring[]Yes–Publicly accessible source URLs
languagestringNo"en"Language code (e.g., en-US, es, fr)
target_duration_minutesnumberNo10Target podcast length (1–120)
stylestringNo"deep_dive"Podcast style – see the nine ids below
voice_configobjectNo2 voicesVoice configuration
tts_modelstringNo"gemini-2.5-flash-tts"Text-to-speech model
auto_publishbooleanNotrue (production), false (test)Auto-publish after generation (prod only)
custom_focusstringNonullCustom focus topic (prod only)
source_weightsobjectNonullSource weighting (prod only)

Note: Only publicly accessible URLs are supported. File uploads (file_ids) and raw text content (text_content) are not available via the API.

Valid style ids – exactly these nine, and nothing else:

deep_dive, critique, didactic, debate, feynman_technique, simplified_explanation, pedagogical_framework, alchemist_formula, references_recall

Any other explicitly-supplied value returns 400 INVALID_STYLE. Omitting the field, or sending null or an empty string, defaults to deep_dive. references_recall (References & Key Data Recall) additionally requires a Creator or Pro plan – any other plan gets 403 TIER_FEATURE_BLOCKED. See the audio styles guide for what each one produces.

Example request:

curl -X POST https://api-ext.podhoc.com/v1/podcasts \
  -H "X-Api-Key: phk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "urls": ["https://example.com/article"],
    "language": "en-US",
    "target_duration_minutes": 10,
    "style": "deep_dive"
  }'

Example response (202 Accepted):

{
  "success": true,
  "data": {
    "podcast_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "status": "processing",
    "estimated_duration_minutes": 10,
    "credits_charged": 38
  },
  "meta": {
    "request_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
    "credits_charged": 38,
    "credit_balance": 462
  }
}

GET /v1/podcasts/{id}/status – Poll generation status

Check the progress of a podcast being generated.

Example request:

curl https://api-ext.podhoc.com/v1/podcasts/a1b2c3d4-e5f6-7890-abcd-ef1234567890/status \
  -H "X-Api-Key: phk_test_..."

Example response:

{
  "success": true,
  "data": {
    "podcast_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "status": "completed",
    "progress": 100,
    "started_at": "2026-03-27T10:00:00+00:00"
  }
}

Status values: requested, processing, completed, failed.

GET /v1/podcasts/{id}/download – Download podcast

Get a presigned download URL for a completed podcast. The URL expires after 1 hour.

Example request:

curl https://api-ext.podhoc.com/v1/podcasts/a1b2c3d4-e5f6-7890-abcd-ef1234567890/download \
  -H "X-Api-Key: phk_test_..."

Example response:

{
  "success": true,
  "data": {
    "download_url": "https://s3.amazonaws.com/...",
    "expires_at": "2026-03-27T11:00:00+00:00",
    "format": "mp3",
    "duration_seconds": 600
  }
}

GET /v1/estimate-cost – Estimate credit cost

Preview the credit cost before creating a podcast.

Query parameters:

ParameterTypeRequiredDefaultDescription
duration_minutesintegerNo10Target podcast duration
source_countintegerNo1Number of content sources
voice_countintegerNo2Number of voices
has_custom_weightsbooleanNofalseWhether custom weights are used

Example request:

curl "https://api-ext.podhoc.com/v1/estimate-cost?duration_minutes=30&source_count=2&voice_count=2" \
  -H "X-Api-Key: phk_test_..."

Example response:

{
  "success": true,
  "data": {
    "base_credits": 114,
    "credit_multiplier": 1.5,
    "final_credits": 171,
    "breakdown": {
      "base_cost": 75,
      "multi_source_bonus": 20,
      "custom_weights_bonus": 0,
      "voice_multiplier": 1.2,
      "subtotal_before_cap": 114,
      "cap_applied": null,
      "tier_max_cost": 500
    },
    "formula": "max(30, ceil(30 x 2.5)) + 20 x 1.2 = 114 x multiplier = 171"
  }
}

GET /v1/account/credits – Check credit balance

Example request:

curl https://api-ext.podhoc.com/v1/account/credits \
  -H "X-Api-Key: phk_test_..."

Example response:

{
  "success": true,
  "data": {
    "credits": 1500,
    "tier": "pro"
  }
}

Pricing

Podcast generation uses dynamic credit pricing. The API applies an additional multiplier based on your token type.

Formula:

Base cost:     max(30, ceil(duration_minutes x 2.5))
Bonuses:       + 20 if more than 1 source
               + 10 if custom weights enabled
Voice scaling: x 1.2 if more than 1 voice
Cap:           min(500, tier_max_cost)
API cost:      base_credits x credit_multiplier

Duration-based cost examples (1 source, 1 voice, no custom weights):

DurationBase costTest token (Cheaper)Prod token (Expensive)
5 min304575
10 min304575
30 min75113188
45 min113170283
60 min150225375
90 min225338500
120 min300450500

Advanced settings impact:

SettingEffectExample
Multiple sources (>1)+20 credits2 URLs adds 20 to base
Custom weights+10 creditsEnabling source weights adds 10
Multiple voices (>1)x1.2 multiplier2 voices multiplies subtotal by 1.2

Credit multipliers:

Token typeMultiplierTypical use
Test (phk_test_...)CheaperDevelopment, testing, integration
Production (phk_prod_...)ExpensiveProduction applications

Credit multipliers (Cheaper for test, Expensive for production) are the default tiers. These may be adjusted – the actual multiplier applied to your request is shown in the response metadata.


Code examples

Python – complete workflow

import requests
import time

API_KEY = "phk_prod_your_token_here"
BASE = "https://api-ext.podhoc.com/v1"
HEADERS = {"X-Api-Key": API_KEY, "Content-Type": "application/json"}

# Step 1: Create a podcast
resp = requests.post(f"{BASE}/podcasts", headers=HEADERS, json={
    "urls": ["https://example.com/article"],
    "language": "en-US",
    "target_duration_minutes": 10,
    "style": "deep_dive",
})
podcast_id = resp.json()["data"]["podcast_id"]
print(f"Created podcast: {podcast_id}")

# Step 2: Poll until complete
while True:
    status_resp = requests.get(
        f"{BASE}/podcasts/{podcast_id}/status",
        headers={"X-Api-Key": API_KEY},
    )
    data = status_resp.json()["data"]
    print(f"Status: {data['status']} ({data['progress']}%)")
    if data["status"] == "completed":
        break
    if data["status"] == "failed":
        raise Exception("Generation failed")
    time.sleep(10)

# Step 3: Download
dl_resp = requests.get(
    f"{BASE}/podcasts/{podcast_id}/download",
    headers={"X-Api-Key": API_KEY},
)
download_url = dl_resp.json()["data"]["download_url"]

audio = requests.get(download_url)
with open("podcast.mp3", "wb") as f:
    f.write(audio.content)
print("Downloaded podcast.mp3")

Node.js – complete workflow

const API_KEY = 'phk_prod_your_token_here';
const BASE = 'https://api-ext.podhoc.com/v1';

async function createAndDownloadPodcast() {
  // Step 1: Create a podcast
  const createRes = await fetch(`${BASE}/podcasts`, {
    method: 'POST',
    headers: { 'X-Api-Key': API_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      urls: ['https://example.com/article'],
      language: 'en-US',
      target_duration_minutes: 10,
      style: 'deep_dive',
    }),
  });
  const { data: created } = await createRes.json();
  console.log(`Created podcast: ${created.podcast_id}`);

  // Step 2: Poll until complete
  while (true) {
    const statusRes = await fetch(
      `${BASE}/podcasts/${created.podcast_id}/status`,
      { headers: { 'X-Api-Key': API_KEY } },
    );
    const { data: status } = await statusRes.json();
    console.log(`Status: ${status.status} (${status.progress}%)`);
    if (status.status === 'completed') break;
    if (status.status === 'failed') throw new Error('Generation failed');
    await new Promise((r) => setTimeout(r, 10000));
  }

  // Step 3: Download
  const dlRes = await fetch(`${BASE}/podcasts/${created.podcast_id}/download`, {
    headers: { 'X-Api-Key': API_KEY },
  });
  const { data: dl } = await dlRes.json();

  const audioRes = await fetch(dl.download_url);
  const fs = await import('fs');
  const buffer = Buffer.from(await audioRes.arrayBuffer());
  fs.writeFileSync('podcast.mp3', buffer);
  console.log('Downloaded podcast.mp3');
}

createAndDownloadPodcast();

curl – quick reference

# Check credit balance
curl https://api-ext.podhoc.com/v1/account/credits \
  -H "X-Api-Key: $API_KEY"

# Estimate cost
curl "https://api-ext.podhoc.com/v1/estimate-cost?duration_minutes=30&source_count=1&voice_count=2" \
  -H "X-Api-Key: $API_KEY"

# Create podcast
curl -X POST https://api-ext.podhoc.com/v1/podcasts \
  -H "X-Api-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://example.com/article"], "target_duration_minutes": 10}'

# Poll status
curl https://api-ext.podhoc.com/v1/podcasts/<podcast_id>/status \
  -H "X-Api-Key: $API_KEY"

# Download
curl https://api-ext.podhoc.com/v1/podcasts/<podcast_id>/download \
  -H "X-Api-Key: $API_KEY"

Rate limits

Token typeRequests/minuteRequests/hourConcurrent generations
Test10601
Production303005

When rate-limited, the response includes a Retry-After header indicating when to retry.


Error codes

HTTP StatusCodeDescription
400INVALID_REQUESTMissing required fields or invalid parameters
400INVALID_DURATIONDuration outside 1–120 minute range
400INVALID_STYLEstyle is not one of the nine canonical ids
400TEST_TOKEN_RESTRICTEDFeature not available with test tokens
401UNAUTHORIZEDInvalid, expired, or revoked API token
402INSUFFICIENT_CREDITSNot enough credits for the request
403TIER_FEATURE_BLOCKEDStyle needs a higher plan (references_recall)
404PODCAST_NOT_FOUNDPodcast not found or not owned by you
429RATE_LIMITEDRate limit exceeded (check Retry-After header)
500INTERNAL_ERRORUnexpected server error

Error response format:

{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Your account has 20 credits but this request requires 113 credits.",
    "details": { "required": 113, "balance": 20 }
  },
  "meta": { "request_id": "uuid" }
}

Test vs production tokens

FeatureTest tokenProduction token
Duration limit5 minutes max120 minutes max
URL limit1 per requestUnlimited
LanguagesEnglish onlyAll 73 languages
VoicesMax 2All voice options
Custom focusNot availableAvailable
Source weightsNot availableAvailable
File uploadsNot supportedNot supported
Auto-publishNot availableAvailable
Credit multiplierCheaperExpensive
Rate limit2/min, 20/hr30/min, 300/hr
Concurrent jobs15

Test tokens are deliberately conservative — graduate to a production token (phk_prod_…) once you’re past the integration phase.