API referenceOne key. Every format.Say what you want; prompts compile server-side.
Everything my tools do goes through this REST API. It's a semantic API: you send structured intent; a graphic spec, an edit instruction, a script; and the server compiles the actual model prompts, runs the generation on the image, video, and voice models behind me, and meters your credits per call.
Authentication
Every authenticated request carries your API key; either as a bearer token or an x-api-key header. Keys are minted on the API keys page (or via the device-pairing flow below) and look like ma_live_ followed by 40 hex characters.
Authorization: Bearer ma_live_<40 hex chars> # — or — x-api-key: ma_live_<40 hex chars>
Base URL: the MARKETING_AGENT_API_URL you deploy the API under (e.g. https://heymarta.ai/api). Examples below use $API.
Rate limits: 300 requests per minute per IP across the API (device pairing is tighter: 10/min to start, 60/min to poll). If the API runs on a sleeping free instance, the first request can take about a minute while it wakes; retry rather than give up.
The credits model
1 credit = $0.01. New accounts start with a 450-credit welcome grant. Every generation is metered per call: credits are reserved when a job starts, then captured for what was actually used (or released if the job errors). Your ledger shows all of it; topup, reserve, capture, release.
graphic / edit / end-card40 credits · ≈ $0.40logo (raster/vector)15 credits · ≈ $0.15image → video12 credits/sec · ≈ $0.12/slip-sync40 credits · ≈ $0.40narration12 credits/1k chars · ≈ $0.12music bed5 credits · ≈ $0.05upload1 credit · ≈ $0.01
Image → video bills per second (12 credits/s), doubling at 1080p; narration bills per 1,000 characters (min 2). A full UGC-style video (stills + motion + narration + lip-sync + music) sums to roughly 315 credits. Insufficient balance returns 402.
Semantic by design
There is no raw model passthrough. You describe what you want; a typed graphic spec, an instruction, a script; and the server compiles the model prompt, constructs every model input itself, prices the call up front, and returns the finished asset. The prompt compiler never ships to clients, and no endpoint accepts arbitrary model parameters: each generation route has a fixed, priced envelope. Failed jobs release their reservation; you only pay for what completes.
Endpoints
/v1/healthAuth: none
Health check
Liveness probe. Returns immediately (once a sleeping instance is awake).
{ "ok": true }curl $API/v1/health
/v1/meAuth: API key
Who am I
Returns the account behind the key, its live balance, and the current price table; per-tool estimates plus the per-call prices of the underlying generation kinds.
{
"email": "you@example.com",
"balance": 450,
"prices": {
"generate_graphic": 40,
"generate_logo": 15,
"edit_image": 40,
"animate_still": 72,
"generate_ugc": 315
},
"endpoint_prices": [
{ "label": "image generate/edit", "credits": 40 },
{ "label": "logo (raster/vector)", "credits": 15 },
{ "label": "image-to-video (per second; ×2 at 1080p)", "credits": 12 },
{ "label": "voiceover (per 1k chars, min 2)", "credits": 12 },
{ "label": "lipsync", "credits": 40 },
{ "label": "music", "credits": 5 }
]
}curl -H "Authorization: Bearer $KEY" $API/v1/me
/v1/credits/balanceAuth: API key
Credit balance
The current balance, computed from the ledger.
{ "balance": 450 }curl -H "Authorization: Bearer $KEY" $API/v1/credits/balance
/v1/credits/historyAuth: API key
Ledger history
The append-only ledger, newest first. Kinds: topup, reserve, capture, release.
{
"entries": [
{
"ts": "2026-07-06T12:00:00Z",
"kind": "capture",
"amount": 40,
"job_id": "8b7f…",
"note": "image generate/edit"
},
{ "ts": "…", "kind": "topup", "amount": 450, "job_id": null, "note": "welcome credits" }
]
}curl -H "Authorization: Bearer $KEY" $API/v1/credits/history
/v1/credits/packsAuth: API key
Credit packs
The purchasable packs, as shown on the dashboard.
{
"packs": {
"starter": { "credits": 500, "usd": 5, "label": "Starter — 500 credits" },
"studio": { "credits": 1200, "usd": 10, "label": "Studio — 1,200 credits (20% bonus)" },
"agency": { "credits": 2600, "usd": 20, "label": "Agency — 2,600 credits (30% bonus)" }
}
}curl -H "Authorization: Bearer $KEY" $API/v1/credits/packs
/v1/credits/checkoutAuth: API key
Buy credits
Creates a Stripe checkout session (test mode) for a pack and returns the hosted payment URL. Credits land in your ledger via a signature-verified webhook once payment completes; nothing else to call.
{ "pack": "studio" }{ "url": "https://checkout.stripe.com/c/pay/cs_test_…" }curl -X POST $API/v1/credits/checkout \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"pack":"studio"}'/v1/device/startAuth: none (pairing flow)
Start device pairing
Browser pairing for terminals and agents; a simplified device grant. The flow: start here to get a human-readable code and a verify_url; open that URL in a browser, where the signed-in user sees the same code and approves this terminal; meanwhile, poll with the long poll_token until the key arrives. Knowing the short code alone never yields a key, and the key is delivered exactly once. Codes expire after 10 minutes. Rate limit: 10/min.
{
"code": "MKQ4-T7WZ",
"poll_token": "<64 hex chars — keep this secret>",
"verify_url": "https://…/connect?code=MKQ4-T7WZ",
"expires_in": 600
}curl -X POST $API/v1/device/start
/v1/device/pollAuth: none (pairing flow)
Poll device pairing
Poll every few seconds with ?token=<poll_token>. Status is one of pending, approved (includes the API key, exactly once; store it immediately), expired, or unknown. Rate limit: 60/min.
{ "status": "pending" }
// then, after the user approves in the browser:
{ "status": "approved", "api_key": "ma_live_…" }curl "$API/v1/device/poll?token=$POLL_TOKEN"
/v1/voicesAuth: API key
Narration voices
The curated roster of narration voices, plus the default. Each voice carries a short tone description so an agent can match the brand. Pick an id from here; unknown names revert to the default.
{
"default": "Rachel",
"voices": [
{ "id": "Rachel", "tone": "warm, calm female — the safe default" },
{ "id": "Brian", "tone": "deep, resonant male" },
{ "id": "River", "tone": "calm, neutral / non-binary" }
]
}curl -H "Authorization: Bearer $KEY" $API/v1/voices
/v1/estimateAuth: API key
Estimate a cost
Price a piece of work before doing it. Query params: tool (one of generate_graphic, generate_logo, edit_image, animate_still, generate_ugc) and count.
{ "tool": "generate_graphic", "count": 9, "credits": 135, "usd": 1.35 }curl -H "Authorization: Bearer $KEY" \ "$API/v1/estimate?tool=generate_graphic&count=9"
/v1/uploadsAuth: API key
Upload a file (1 credit)
Upload a local file (say, a product photo) as base64 and get back a URL the models can read. 1 credit per upload; max 30 MB decoded.
{ "filename": "product.jpg", "data_b64": "<base64-encoded bytes>" }{ "url": "https://…/product.jpg", "charged": 1 }curl -X POST $API/v1/uploads \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d "{\"filename\":\"product.jpg\",\"data_b64\":\"$(base64 -i product.jpg)\"}"/v1/generate/graphicAuth: API key
Generate a branded graphic (40 credits)
The flagship endpoint. Send a typed spec; what the piece says, how it should feel, and where things sit; and the server compiles it into a model prompt and renders it on the image model. Alternatively, pass a verbatim prompt string instead of a spec. Adding ref_urls (e.g. an uploaded product photo) turns the call into a reference-guided render that keeps the exact real objects.
{
"spec": {
"highLevel": "Instagram square announcing a summer tasting menu",
"style": {
"aesthetics": "warm editorial minimalism", // required
"lighting": "soft morning window light", // required
"medium": "graphic_design", // optional: graphic_design | illustration |
// photograph | 3d_render | painting
"art_style": "flat shapes, generous whitespace", // optional
"palette": ["#FBF7F1", "#B5673F", "#33303A"] // optional hex palette
},
"background": "cream paper texture with subtle grain",
"objects": [ // optional placed elements
{ "desc": "a ceramic espresso cup, steam curling", "bbox": [300, 250, 700, 750] }
],
"texts": [ // optional text zones
{ "text": "Summer, plated.", "desc": "large elegant serif headline",
"bbox": [60, 80, 240, 920], "palette": ["#33303A"] }
]
},
"brand": { // optional brand block woven into the prompt
"name": "Casa Lumi", "palette": ["#FBF7F1", "#B5673F"],
"serif": "high-contrast editorial serif", "sans": "rounded humanist sans",
"ink": "#33303A", "accent": "#B5673F", "voice": "calm, warm, confident"
},
"guidance": "keep it airy; no clutter", // optional free-text steer
"aspect_ratio": "1:1", // e.g. 1:1, 4:5, 9:16
"resolution": "2K", // up to 2K (the priced envelope)
"output_format": "png",
"seed": 42, // optional, for reproducibility
"ref_urls": ["https://…/product.jpg"] // optional → reference-guided render
}bbox zones are [y_min, x_min, y_max, x_max], normalized 0–1000; they pin text and objects to layout regions. One image per call; the compiled prompt never leaves the server.
{ "url": "https://…/graphic.png", "seed": 42, "charged": 40, "job_id": "8b7f…" }curl -X POST $API/v1/generate/graphic \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{
"spec": {
"highLevel": "Instagram square announcing a summer tasting menu",
"style": { "aesthetics": "warm editorial minimalism",
"lighting": "soft morning window light" },
"background": "cream paper texture with subtle grain",
"texts": [{ "text": "Summer, plated.",
"desc": "large elegant serif headline",
"bbox": [60, 80, 240, 920] }]
},
"aspect_ratio": "1:1"
}'/v1/generate/logoAuth: API key
Generate a logo (15 credits)
Renders a logo from a description. The default path produces a clean vector render (style defaults to vector_illustration); optionally pin brand colors as hex values. Setting "model": "raster" switches to a flat raster render on the general image model instead (40 credits).
{
"prompt": "minimal monogram for a coffee studio, letterform C",
"colors": ["#B5673F", "#33303A"], // optional hex palette
"style": "vector_illustration", // optional
"size": "square_hd" // optional
}{ "url": "https://…/logo.svg", "charged": 15, "job_id": "1c2d…" }curl -X POST $API/v1/generate/logo \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"minimal monogram for a coffee studio","colors":["#B5673F"]}'/v1/generate/end-cardAuth: API key
Generate an end card (40 credits)
The closing frame for a UGC-style video: a clean 9:16 card carrying your closing lines (brand name, offer, call to action). Pass the lines; the server composes the card prompt and renders it on the image model.
{ "lines": ["Casa Lumi", "Summer menu — now booking", "casalumi.example"] }{ "url": "https://…/end-card.jpg", "charged": 40, "job_id": "9e0a…" }curl -X POST $API/v1/generate/end-card \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"lines":["Casa Lumi","Summer menu — now booking"]}'/v1/generate/editAuth: API key
Edit an image (40 credits)
Edit one or more existing images by plain-language instruction; relight, restage, swap a background, place a product into a scene. Both fields are required; input images by URL (upload local files first via /v1/uploads).
{
"instruction": "place the product on a marble counter in soft daylight",
"image_urls": ["https://…/product.jpg"],
"aspect_ratio": "4:5", // optional
"resolution": "2K" // optional, up to 2K
}{ "url": "https://…/edited.png", "charged": 40, "job_id": "4f5a…" }curl -X POST $API/v1/generate/edit \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"instruction":"place the product on a marble counter",
"image_urls":["https://…/product.jpg"]}'/v1/generate/i2vAuth: API key
Animate a still (12 credits/sec, ×2 at 1080p)
Image → video on the video model. Give it a still and (optionally) a motion prompt; get back a clip. duration_sec is clamped to 5–15; resolution is 720p (default) or 1080p. Pricing: 12 credits per second at 720p (a 5s clip ≈ 60); 1080p bills ×2. Requests above 15s or 1080p are rejected.
{
"image_url": "https://…/hero.png",
"prompt": "slow push-in, steam drifting, shallow depth of field", // optional
"duration_sec": 5, // 5–15, billed per second
"resolution": "720p", // 720p | 1080p (1080p bills ×2)
"audio": true // optional ambient audio; off by default
}{ "url": "https://…/clip.mp4", "charged": 60, "job_id": "7a1b…" }curl -X POST $API/v1/generate/i2v \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"image_url":"https://…/hero.png","duration_sec":5}'/v1/generate/lipsyncAuth: API key
Lip-sync a clip (40 credits)
Sync a talking-head clip to a narration track: mouth movement is re-rendered to match the audio. Both URLs are required; typically the video comes from /v1/generate/i2v and the audio from /v1/generate/tts.
{ "video_url": "https://…/clip.mp4", "audio_url": "https://…/vo.mp3" }{ "url": "https://…/synced.mp4", "charged": 40, "job_id": "3d9c…" }curl -X POST $API/v1/generate/lipsync \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"video_url":"https://…/clip.mp4","audio_url":"https://…/vo.mp3"}'/v1/generate/ttsAuth: API key
Narration (12 credits / 1k chars, min 2)
Text → narration, with word-level timestamps in the response so you can time captions to the voice. Pick a voice id from /v1/voices; unknown names revert to the default server-side. Up to 2,500 characters per call. Use previous_text / next_text for consistent delivery across a multi-part script.
{
"text": "Golden hour at Casa Lumi. The summer menu is here.",
"voice": "Rachel", // optional, from /v1/voices
"stability": 0.45, // optional, 0–1
"style": 0.35, // optional, 0–1
"speed": 1.0, // optional
"language": "es", // optional ISO 639-1
"previous_text": "…", // optional continuity context
"next_text": "…"
}{
"audio_url": "https://…/vo.mp3",
"timestamps": [ { "word": "Golden", "start": 0.0, "end": 0.42 }, … ],
"charged": 2,
"job_id": "5b6e…"
}curl -X POST $API/v1/generate/tts \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Golden hour at Casa Lumi.","voice":"Rachel"}'/v1/generate/musicAuth: API key
Music bed (5 credits)
A background music track from a text brief. duration_sec should match your video length; the server pads it slightly for clean fades (minimum 10s, max ~90s per call).
{ "prompt": "warm acoustic morning café, light percussion, unhurried", "duration_sec": 20 }{ "audio_url": "https://…/music.mp3", "charged": 5, "job_id": "6c7d…" }curl -X POST $API/v1/generate/music \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"warm acoustic morning café","duration_sec":20}'Across all generation endpoints: invalid input returns 400, a missing or bad key 401, insufficient balance 402, and an upstream model error 502; with the reservation released.
New here? Start with the getting-started guide.