Official SDKs
Official SDKs for five languages
Install one package and call image, video and audio models from your own server. The SDK handles authentication and waiting for results, so you only write the request and read the answer.
What the SDK does for you
You write one function call. The SDK signs the request, uploads your files, waits for the result and hands it back as typed data.
- Your codeOne function call
- Official SDKSigns requests, uploads files, waits for results
- Image, video and audio modelsSame models and prices as the website
Good for
- Backend servicesGenerate media inside your own API or job queue.
- Scripts and batch jobsCreate many tasks from a spreadsheet or a scheduled job.
- Apps with a serverKeep the key on the server and send finished files to users.
Get started in three steps
- Step 1
Create an API key
Make one in the console and store it as SPICY_API_KEY on your server.
- Step 2
Install the package
Pick your language below and run the install line.
- Step 3
Create a task, read the result
Send a prompt, wait for the task, then save the file it returns.
Pick your language
Every SDK is published to its language's main registry and talks to the same API. Switch tabs to see the install line and a complete request.
TypeScript
@spicyapi/sdkRequires Node.js 22.13+
Your server creates tasks and stores results. Install, export the key, run.
npm install @spicyapi/sdk
export SPICY_API_KEY="injected from your secret manager"import { randomUUID } from "node:crypto";
import { SpicyClient } from "@spicyapi/sdk";
const client = new SpicyClient();
const model = await client.getModel("MODEL_ID_FROM_CATALOG");
const task = await client.run(
{ model: model.model, input: { prompt: "A cinematic night portrait" } },
{ idempotencyKey: randomUUID() },
);
console.log(task.state, task.output?.assets?.[0]?.url);Python
spicyapiRequires Python 3.11+
The same sequence in Python shape: quote, create, wait for a terminal state.
pip install spicyapi
export SPICY_API_KEY="injected from your secret manager"import uuid
from spicyapi import SpicyClient, output_assets
client = SpicyClient() # reads SPICY_API_KEY from the environment
model = client.get_model("MODEL_ID_FROM_CATALOG")
quote = client.quote_task(
model=model["model"],
input_data={"prompt": "A cinematic night portrait"},
)
print(quote["estimatedCost"], quote["maxCharge"]) # decide before you spend
task = client.create_task(
model=model["model"],
input_data={"prompt": "A cinematic night portrait"},
idempotency_key=str(uuid.uuid4()),
quote_id=quote["quoteId"],
expected_cost=quote["estimatedCost"],
)
final = client.wait_for_terminal(task["taskId"])
for asset in output_assets(final):
print(asset["url"])Go
github.com/spicyapi-ai/spicy-goRequires Go 1.23+
Standard library only, so nothing new enters your module graph. Available right now with go get.
go get github.com/spicyapi-ai/spicy-go
export SPICY_API_KEY="injected from your secret manager"client, err := spicy.New(spicy.Options{}) // reads SPICY_API_KEY from the environment
if err != nil {
return err
}
request := spicy.CreateTaskInput{
Model: "MODEL_ID_FROM_CATALOG",
Input: map[string]any{"prompt": "A cinematic night portrait"},
}
quote, err := client.Quote(ctx, request)
if err != nil {
return err
}
request.QuoteID = quote.QuoteID
request.ExpectedCost = quote.EstimatedCost
// Store this next to your own record of the job before sending it: its whole
// purpose is to still be there after the crash that lost the response.
key, err := spicy.NewIdempotencyKey()
if err != nil {
return err
}
task, err := client.CreateTask(ctx, request, key, spicy.CreateTaskOptions{})
if err != nil {
return err
}
final, err := client.WaitForTerminal(ctx, task.TaskID, spicy.WaitOptions{})
if err != nil {
return err
}
for _, asset := range final.ReadyAssets() {
log.Println(asset.URL)
}PHP
spicyapi/spicyapiRequires PHP 8.1+, ext-json, ext-hash
Associative arrays and named arguments, with no library dependencies.
composer require spicyapi/spicyapiuse SpicyApi\SpicyClient;
$client = new SpicyClient(); // reads SPICY_API_KEY from the environment
$quote = $client->quote('MODEL_ID_FROM_CATALOG', ['prompt' => 'A cinematic night portrait']);
printf("at most %s\n", $quote['maxCharge']); // decide before you spend
$task = $client->createTask(
model: $quote['model'],
input: ['prompt' => 'A cinematic night portrait'],
idempotencyKey: SpicyClient::idempotencyKey(),
quoteId: $quote['quoteId'],
expectedCost: $quote['estimatedCost'],
);
$final = $client->waitForTerminal($task['taskId']);
foreach (SpicyClient::readyAssets($final) as $asset) {
echo $asset['url'], "\n";
}Java
ai.spicyapi:spicyapi-java:0.1.0Requires Java 17+, Jackson
Records and Jackson, and run() covers create and wait in one call.
<dependency>
<groupId>ai.spicyapi</groupId>
<artifactId>spicyapi-java</artifactId>
<version>0.1.0</version>
</dependency>SpicyClient client = SpicyClient.builder().apiKeyFromEnvironment().build();
CreateTaskRequest request = CreateTaskRequest.of(
"MODEL_ID_FROM_CATALOG",
Map.of("prompt", "A cinematic night portrait"));
TaskQuote quote = client.quote(request); // free; reserves nothing
System.out.println("this will cost at most " + quote.maxCharge());
TaskRecord task = client.run( // createTask + waitForTerminal
request.withQuote(quote), UUID.randomUUID().toString());
// Transcription and similar endpoints answer in text and return no files at all.
task.outputText().ifPresent(System.out::println);
for (TaskAsset asset : task.assets()) {
asset.url().ifPresent(System.out::println);
}What happens after you submit
Image and video generation keeps running after the create call returns. Save the task ID, then let a webhook or a bounded status check tell you when it is done.
- createTaskSubmit with an idempotency key; one generation is accepted once.
- taskIdThe response comes back immediately while the task keeps running.
- Webhook / recordInfoA signed webhook first; bounded polling only to recover.
- output.assetsReady-to-use URLs. Keep your own copy of anything that matters.
Calling from a browser or mobile app
A key inside a client app is a public key. Keep it on your server: @spicyapi/proxy adds the credential to requests from your front end, so the app never sees it.
- Key inside the appAnyone can read it and spend your balance.
- App with the key inside
- SpicyAPI
- Key on your serverThe app talks to your server, and the proxy adds the key.
- Browser or phone
- Your server + proxy
- SpicyAPI
npm install @spicyapi/proxy// app/api/spicy/proxy/route.ts
export { GET, POST } from "@spicyapi/proxy/nextjs";
export const runtime = "nodejs";Before you go live
- 01Confirm the model is currently available.
- 02Validate the exact fields shown on its model page.
- 03Keep the API key on the server.
- 04Show the expected cost before submission.
- 05Persist taskId outside the browser session.
- 06Verify webhook signatures or bound status checks.
- 07Treat an accepted task as final and non-cancelable.
Fix common errors
Most first-time problems are one of these three. Each card shows what you see and the one thing to change.
- 401
Invalid API key
No key or a wrong one reached the API. Set SPICY_API_KEY in the same environment your code runs in.
- wait
Task still running
Videos can take a few minutes. Wait with a time limit or use a webhook. Resubmitting without the same Idempotency-Key creates a second paid task.
- link
Result link expired
Download links are short-lived. Ask for a fresh link for the task instead of storing the old one.
