API de imágenes de producto de HedaAI
Genera 12 imágenes profesionales de e-commerce (8 imágenes principales + 4 banners A+) y el texto completo del listing a partir de fotos de producto — de forma programática o directamente desde tu agente de IA vía MCP.
https://hedaai.com/api/v1 · Réplicas de docs para agentes: /developers/api.md · /developers/llms.txtAutenticación
Crea una API key en hedaai.com/settings/api-keys (sirve cualquier cuenta; las cuentas nuevas reciben un bono de registro de $2.00 ≈ 2 tareas gratis). La key completa (hda_live_…) y su webhook secret (whsec_…) se muestran una sola vez al crearla — guárdalos de forma segura. Las keys se pueden nombrar, limitar por gasto y revocar en cualquier momento.
Authorization: Bearer hda_live_...
Las keys se almacenan hasheadas y están vinculadas solo a tu cuenta — una API key nunca puede leer los datos de otra cuenta.
GET /v1/account informa "mode": "sandbox_watermarked"). Tu primer pago elimina automáticamente las marcas de agua de todos los productos generados anteriormente.Límites de gasto: cada key tiene un spend_cap_cents opcional (por defecto: ilimitado). Al alcanzarlo, la creación de tareas falla con spend_cap_exceeded (403).
Envoltorio de respuesta
{ "status": 200, "code": 200, "message": "", "data": { … } }
Los errores añaden un error_code para hacer coincidencias programáticas. Errores de transporte vs. fallos de tarea: un HTTP 4xx/5xx significa que falló la solicitud. Una tarea que falla durante la generación no es un error HTTP — el polling devuelve 200 con status: "failed" y un campo error, y no se te cobra.
Inicio rápido
# 1. Crear una tarea
curl -X POST https://hedaai.com/api/v1/tasks \
-H "Authorization: Bearer $HEDAAI_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-12345-attempt-1" \
-d '{
"product_name": "stainless steel insulated water bottle",
"images": ["https://your-cdn.example.com/bottle-front.jpg"],
"aspect_ratio": "1:1",
"category": "insulated water bottle 750ml",
"target_market": "amazon_us"
}'
# → data.task_id = "3f8a…", data.status = "queued"
# 2. Consultar cada 15–30s hasta estado terminal
curl -H "Authorization: Bearer $HEDAAI_API_KEY" https://hedaai.com/api/v1/tasks/3f8a…
# → data.status = "succeeded", data.product_id = "9c1b…"
# 3. Obtener el producto final (12 URLs de imagen + texto del listing)
curl -H "Authorization: Bearer $HEDAAI_API_KEY" https://hedaai.com/api/v1/products/9c1b…
POST/v1/tasks
Crea una tarea de generación. Header opcional: Idempotency-Key (≤64 caracteres, ver más abajo).
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
product_name | string | sí | Nombre corto del producto. Señal de grounding fuerte — sé específico. |
images | string[1–8] | sí | URLs de fotos (http/https públicas, ≤15MB cada una) y/o base64 / data-URIs. Las fotos multiángulo mejoran la fidelidad. |
aspect_ratio | string | sí | 1:1, 2:3, 3:2, 3:4, 4:3, 9:16, 16:9. Las imágenes principales de Amazon usan 1:1. |
category | string | no | Categoría concreta, p. ej. "filtro de repuesto para aspiradora". Recomendado. |
brand_name | string | no | Alimenta las imágenes de historia de marca y el texto del listing. |
key_features | string[≤10] | no | Puntos de venta que deben mostrarse. |
specs | object | no | {material, colorway, key_components[], usage_context, use_scenarios[], dimensions[], scale}. Recomendado — la API no tiene paso de revisión interactiva; el grounding viene de lo que envías. |
target_market | string | no | amazon_us (por defecto) · shopify · ebay |
copy_language | string | no | Locale del texto del listing (en-US, de-DE, ja-JP, …). Por defecto, según el mercado objetivo. |
aplus_format | string | no | banner (16:9, por defecto) · aplus_3_4 · aplus_9_16 · standard_aplus/premium (solo Amazon) |
no_human_models | bool | no | true = sin personas en ninguna imagen. |
supplementary_text | string ≤5000 | no | Especificaciones en texto libre / extractos del manual para que la IA los lea. |
callback_url | string | no | Webhook para el estado terminal (Webhooks). |
Devuelve un objeto task:
{
"task_id": "3f8a…", "status": "queued", "progress": 0,
"queue_position": 2, "eta_seconds": 840, "cost_cents": 0,
"created_at": "2026-07-17T08:00:00Z",
"urls": { "get": "/api/v1/tasks/3f8a…", "cancel": "/api/v1/tasks/3f8a…/cancel" }
}
GET/v1/tasks/{task_id}
Ciclo de vida del estado: queued → processing → succeeded | partial_failed | failed | canceled.
- succeeded — las 12 imágenes listas;
product_idasignado; se cobrancost_cents: 100. - partial_failed — algunas imágenes fallaron; el producto con las imágenes exitosas se guarda igualmente; no se cobra.
- failed / canceled — no se cobra;
errorexplica el motivo.
Haz polling cada 15–30 segundos. queue_position / eta_seconds se rellenan mientras está en cola.
POST/v1/tasks/{task_id}/cancel
Cancela una tarea. Gratis mientras está en cola; las tareas ya terminadas no se ven afectadas (idempotente).
GET/v1/products/{product_id}
{
"product_id": "9c1b…", "name": "stainless steel insulated water bottle",
"target_market": "amazon_us", "aspect_ratio": "1:1", "watermarked": false,
"listing_copy": {
"title": "…", "bullet_points": ["…","…","…","…","…"], "description": "…",
"search_terms": "…", "meta_title": "…", "meta_description": "…", "language": "en"
},
"images": [
{ "slice_id": "M1", "section": "main", "url": "https://images.hedaai.com/…",
"url_2k": "https://images.hedaai.com/…", "width": 2048, "height": 2048, "sort_order": 1 }
],
"created_at": "2026-07-17T08:11:32Z"
}
M1–M8son las 8 imágenes principales;A1–A4, los banners A+.url_2kaparece en cuentas de pago cuando termina el escalado 2K asíncrono (solo imágenes principales) — puede tardar un par de minutos más quesucceeded.- Justo después de
succeeded, una fila de imagen puede quedar pendiente un instante — vuelve a consultar unos segundos después si ves menos de 12 imágenes. - Las URLs son permanentes (se sirven desde nuestro CDN); el hotlinking y la copia están permitidos.
GET/v1/account
{
"balance_cents": 1250, "paid_balance_cents": 1000, "bonus_balance_cents": 250,
"has_paid": true, "mode": "live", "price_per_task_cents": 100,
"key": { "name": "prod-integration", "key_prefix": "hda_live_ab12…cd34",
"spend_cap_cents": null, "spent_cents": 700 }
}
Idempotencia
Envía un header Idempotency-Key (≤64 caracteres) en POST /v1/tasks. Reintentar con la misma key y el mismo body devuelve la tarea original en lugar de crear (y acabar cobrando) un duplicado. La misma key con un body distinto devuelve 409 idempotency_conflict. Muy recomendable para agentes y automatización — los reintentos de red dejan de suponer un riesgo de doble cobro.
Webhooks
Define callback_url al crear la tarea para recibir un POST en el estado terminal. Eventos: task.succeeded, task.partial_failed, task.failed, task.canceled.
{ "id": "evt_…", "type": "task.succeeded", "created_at": "2026-07-17T08:11:35Z",
"data": { /* objeto task — misma forma que GET /v1/tasks/{id} */ } }
Las firmas siguen la especificación Standard Webhooks — headers webhook-id, webhook-timestamp, webhook-signature: v1,<base64>. Verifícalas con el webhook_secret de tu key:
import hmac, hashlib, base64
def verify(secret, msg_id, timestamp, body: bytes, signature_header):
key = base64.b64decode(secret.removeprefix("whsec_"))
expected = base64.b64encode(hmac.new(key, f"{msg_id}.{timestamp}.".encode() + body,
hashlib.sha256).digest()).decode()
return any(hmac.compare_digest(expected, s.split(",", 1)[1])
for s in signature_header.split() if s.startswith("v1,"))
Responde rápido con cualquier 2xx (haz el trabajo pesado de forma asíncrona). Entrega: un intento inmediato, reintentos a los 3s / 6s / 12s y luego se marca como fallida (el polling sigue funcionando). Rechaza timestamps antiguos (>5 min) y deduplica por id. callback_url debe ser un endpoint http(s) público — las direcciones privadas/internas se rechazan.
Límites de tasa y concurrencia
| Límite | Valor | Al superarlo |
|---|---|---|
| Solicitudes por key | 120 / min (todos los endpoints) | 429 rate_limited — revisa los headers RateLimit-Limit / -Remaining / -Reset |
| Tareas en curso por key | 2 (queued + processing) | 429 concurrency_limit — espera a que termine una tarea |
| Capacidad de la plataforma | dinámica | 429 api_capacity — reintenta en unos minutos |
La generación es pesada (13–20 llamadas de imagen a la IA por tarea), así que el throughput lo gobiernan los slots de tareas en curso y no la tasa de solicitudes. ¿Necesitas más concurrencia? Contáctanos.
Errores
| error_code | HTTP | Significado |
|---|---|---|
bad_request | 400 | Parámetros inválidos — el mensaje señala el campo exacto (p. ej. images[2]: image download failed with HTTP 403). |
unauthorized | 401 | API key ausente/inválida, o key revocada. |
insufficient_credits | 402 | Saldo insuficiente; se incluye data.balance_cents. |
spend_cap_exceeded | 403 | El límite de gasto de esta key está agotado. |
account_disabled / email_not_verified / email_bounced | 403 | Restricciones a nivel de cuenta. |
not_found | 404 | La tarea o el producto no existe, o pertenece a otra cuenta. |
idempotency_conflict | 409 | Mismo Idempotency-Key, body distinto. |
concurrency_limit / api_capacity / rate_limited | 429 | Ver límites. |
generation_failed / internal_error | 500 | Fallo del servidor; es seguro reintentar con una idempotency key. |
Ejemplos de código
El flujo completo — crear una tarea, hacer polling hasta el estado terminal y obtener el producto final — en el lenguaje que prefieras.
API=https://hedaai.com/api/v1
# 1. Crear una tarea
TASK_ID=$(curl -sS -X POST "$API/tasks" \
-H "Authorization: Bearer $HEDAAI_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sku-8842-v1" \
-d '{"product_name":"stainless steel insulated water bottle",
"images":["https://your-cdn.example.com/bottle.jpg"],
"aspect_ratio":"1:1"}' | jq -r .data.task_id)
# 2. Consultar hasta estado terminal
while :; do
TASK=$(curl -sS -H "Authorization: Bearer $HEDAAI_API_KEY" "$API/tasks/$TASK_ID")
STATUS=$(echo "$TASK" | jq -r .data.status)
[ "$STATUS" = "queued" ] || [ "$STATUS" = "processing" ] || break
sleep 20
done
# 3. Obtener imágenes + texto del listing
if [ "$STATUS" = "succeeded" ]; then
PRODUCT_ID=$(echo "$TASK" | jq -r .data.product_id)
curl -sS -H "Authorization: Bearer $HEDAAI_API_KEY" "$API/products/$PRODUCT_ID" \
| jq -r '.data.images[].url, .data.listing_copy.title'
fi
import time, requests
API = "https://hedaai.com/api/v1"
H = {"Authorization": "Bearer hda_live_..."}
# 1. Crear una tarea
task = requests.post(f"{API}/tasks",
headers={**H, "Idempotency-Key": "sku-8842-v1"},
json={"product_name": "stainless steel insulated water bottle",
"images": ["https://your-cdn.example.com/bottle.jpg"],
"aspect_ratio": "1:1"}).json()["data"]
# 2. Consultar hasta estado terminal
while task["status"] in ("queued", "processing"):
time.sleep(20)
task = requests.get(f"{API}/tasks/{task['task_id']}", headers=H).json()["data"]
# 3. Obtener imágenes + texto del listing
if task["status"] == "succeeded":
product = requests.get(f"{API}/products/{task['product_id']}", headers=H).json()["data"]
print([img["url"] for img in product["images"]])
print(product["listing_copy"]["title"])
const API = "https://hedaai.com/api/v1";
const H = { Authorization: "Bearer hda_live_...", "Content-Type": "application/json" };
// 1. Crear una tarea
let { data: task } = await (await fetch(`${API}/tasks`, {
method: "POST",
headers: { ...H, "Idempotency-Key": "sku-8842-v1" },
body: JSON.stringify({
product_name: "stainless steel insulated water bottle",
images: ["https://your-cdn.example.com/bottle.jpg"],
aspect_ratio: "1:1",
}),
})).json();
// 2. Consultar hasta estado terminal
while (["queued", "processing"].includes(task.status)) {
await new Promise(r => setTimeout(r, 20000));
({ data: task } = await (await fetch(`${API}/tasks/${task.task_id}`, { headers: H })).json());
}
// 3. Obtener imágenes + texto del listing
if (task.status === "succeeded") {
const { data: product } = await (await fetch(`${API}/products/${task.product_id}`, { headers: H })).json();
console.log(product.images.map(i => i.url), product.listing_copy.title);
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"time"
)
const api = "https://hedaai.com/api/v1"
type envelope struct {
Data struct {
TaskID string `json:"task_id"`
Status string `json:"status"`
ProductID string `json:"product_id"`
Images []struct {
URL string `json:"url"`
} `json:"images"`
ListingCopy struct {
Title string `json:"title"`
} `json:"listing_copy"`
} `json:"data"`
}
func call(method, url string, body []byte, extra map[string]string) (envelope, error) {
var out envelope
var rdr *bytes.Reader
if body != nil {
rdr = bytes.NewReader(body)
} else {
rdr = bytes.NewReader(nil)
}
req, err := http.NewRequest(method, url, rdr)
if err != nil {
return out, err
}
req.Header.Set("Authorization", "Bearer hda_live_...")
req.Header.Set("Content-Type", "application/json")
for k, v := range extra {
req.Header.Set(k, v)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return out, err
}
defer resp.Body.Close()
return out, json.NewDecoder(resp.Body).Decode(&out)
}
func main() {
// 1. Crear una tarea
payload, _ := json.Marshal(map[string]any{
"product_name": "stainless steel insulated water bottle",
"images": []string{"https://your-cdn.example.com/bottle.jpg"},
"aspect_ratio": "1:1",
})
task, err := call("POST", api+"/tasks", payload,
map[string]string{"Idempotency-Key": "sku-8842-v1"})
if err != nil {
panic(err)
}
// 2. Consultar hasta estado terminal
for task.Data.Status == "queued" || task.Data.Status == "processing" {
time.Sleep(20 * time.Second)
if task, err = call("GET", api+"/tasks/"+task.Data.TaskID, nil, nil); err != nil {
panic(err)
}
}
// 3. Obtener imágenes + texto del listing
if task.Data.Status == "succeeded" {
product, err := call("GET", api+"/products/"+task.Data.ProductID, nil, nil)
if err != nil {
panic(err)
}
for _, img := range product.Data.Images {
fmt.Println(img.URL)
}
fmt.Println(product.Data.ListingCopy.Title)
}
}
import java.net.URI;
import java.net.http.*;
import java.time.Duration;
import com.fasterxml.jackson.databind.*; // sirve cualquier librería JSON
public class HedaAI {
static final String API = "https://hedaai.com/api/v1";
static final HttpClient CLIENT = HttpClient.newHttpClient();
static final ObjectMapper MAPPER = new ObjectMapper();
static JsonNode call(HttpRequest.Builder b) throws Exception {
HttpRequest req = b.header("Authorization", "Bearer hda_live_...")
.header("Content-Type", "application/json")
.build();
String body = CLIENT.send(req, HttpResponse.BodyHandlers.ofString()).body();
return MAPPER.readTree(body).path("data");
}
public static void main(String[] args) throws Exception {
// 1. Crear una tarea
JsonNode task = call(HttpRequest.newBuilder(URI.create(API + "/tasks"))
.header("Idempotency-Key", "sku-8842-v1")
.POST(HttpRequest.BodyPublishers.ofString("""
{"product_name":"stainless steel insulated water bottle",
"images":["https://your-cdn.example.com/bottle.jpg"],
"aspect_ratio":"1:1"}""")));
// 2. Consultar hasta estado terminal
String status = task.path("status").asText();
while (status.equals("queued") || status.equals("processing")) {
Thread.sleep(Duration.ofSeconds(20));
task = call(HttpRequest.newBuilder(
URI.create(API + "/tasks/" + task.path("task_id").asText())).GET());
status = task.path("status").asText();
}
// 3. Obtener imágenes + texto del listing
if (status.equals("succeeded")) {
JsonNode product = call(HttpRequest.newBuilder(
URI.create(API + "/products/" + task.path("product_id").asText())).GET());
product.path("images").forEach(img -> System.out.println(img.path("url").asText()));
System.out.println(product.path("listing_copy").path("title").asText());
}
}
}
<?php
$api = "https://hedaai.com/api/v1";
$headers = ["Authorization: Bearer hda_live_...", "Content-Type: application/json"];
function call($method, $url, $headers, $body = null) {
$ch = curl_init($url);
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method, CURLOPT_HTTPHEADER => $headers]);
if ($body !== null) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
$res = json_decode(curl_exec($ch), true); curl_close($ch);
return $res["data"];
}
// 1. Crear una tarea
$task = call("POST", "$api/tasks", array_merge($headers, ["Idempotency-Key: sku-8842-v1"]), [
"product_name" => "stainless steel insulated water bottle",
"images" => ["https://your-cdn.example.com/bottle.jpg"],
"aspect_ratio" => "1:1",
]);
// 2. Consultar hasta estado terminal
while (in_array($task["status"], ["queued", "processing"])) {
sleep(20);
$task = call("GET", "$api/tasks/{$task['task_id']}", $headers);
}
// 3. Obtener imágenes + texto del listing
if ($task["status"] === "succeeded") {
$product = call("GET", "$api/products/{$task['product_id']}", $headers);
foreach ($product["images"] as $img) echo $img["url"], "\n";
echo $product["listing_copy"]["title"], "\n";
}
Servidor MCP (para agentes de IA)
HedaAI ofrece un servidor Model Context Protocol alojado para que los agentes generen imágenes de producto directamente:
- Endpoint:
https://hedaai.com/api/v1/mcp(Streamable HTTP, sin estado) - Auth: el mismo header
Authorization: Bearer hda_live_… - Herramientas:
create_product_shoot·get_task·get_product·get_balance
claude mcp add --transport http hedaai https://hedaai.com/api/v1/mcp \
--header "Authorization: Bearer $HEDAAI_API_KEY"
{
"mcpServers": {
"hedaai": {
"url": "https://hedaai.com/api/v1/mcp",
"headers": { "Authorization": "Bearer hda_live_..." }
}
}
}
La generación es asíncrona: los agentes llaman a create_product_shoot (opcionalmente con un idempotency_key), hacen polling de get_task cada 30–60s hasta el estado terminal y luego get_product para obtener las URLs de las imágenes y el texto del listing. Los clientes que requieren OAuth (p. ej. los conectores personalizados de claude.ai) aún no son compatibles — usa clientes que admitan API key, como Claude Code, Cursor o SDKs de agentes.
Precios
| Concepto | Precio |
|---|---|
| Sesión de producto — 12 imágenes + texto del listing | $1.00, se cobra solo si tiene éxito |
| Bono de registro | $2.00 (≈ 2 tareas gratis, con marca de agua hasta el primer pago) |
| Recarga de saldo | $10–$1000 en hedaai.com; ≥$50 +5% de bono, ≥$100 +10% |
El saldo nunca caduca. Sin suscripción. El precio vigente siempre está disponible en GET /v1/account (price_per_task_cents).