Nightjar for developers
Build complete image workflows with one API.
Upload source images, organize Products, author reusable creative ingredients, and run every Nightjar workflow programmatically.
Start here
Your first authenticated request
An active Nightjar Subscription includes API access. A Team owner creates a read-only or full API Credential in Settings → API. The key is shown once, so store it in a secret manager and never put it in source control.
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/team"End cURL example.const response = await fetch('https://api.nightjar.so/v1/team', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/team', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/team',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.A successful response identifies the Team, the credential profile, remaining Credits, and the effective Team-wide API concurrency budget. Every response carries a Request-Id header. If you contact support, send the Request-Id, endpoint, and approximate time; never send the API Key.
For coding agents
Complete runnable examples
Upload → Product → Product Photography → poll → output Asset.
Runs against production
NIGHTJAR_API_KEY and NIGHTJAR_IMAGE_PATH. Running an example creates real resources in your Library and spends one Credit on a 1K image. Every example is verified against the contract in CI.#!/usr/bin/env bash
set -euo pipefail
: "${NIGHTJAR_API_KEY:?NIGHTJAR_API_KEY is required.}"
: "${NIGHTJAR_IMAGE_PATH:?NIGHTJAR_IMAGE_PATH is required.}"
API_BASE="${NIGHTJAR_BASE_URL:-https://api.nightjar.so/v1}"
idempotency_key() {
printf '%s-%s-%s-%s' "$1" "$(date +%s)" "$$" "${RANDOM}"
}
api_curl() {
curl --silent --show-error --fail-with-body \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"$@"
}
IMAGE_SIZE="$(wc -c < "${NIGHTJAR_IMAGE_PATH}" | tr -d ' ')"
UPLOAD_SESSION="$(api_curl \
--request POST \
--header 'Content-Type: application/json' \
--header "Idempotency-Key: $(idempotency_key upload)" \
--data "$(jq -cn --argjson size "${IMAGE_SIZE}" \
'{content_type:"image/jpeg",size_bytes:$size}')" \
"${API_BASE}/uploads")"
UPLOAD_TOKEN="$(curl --silent --show-error --fail-with-body \
--request "$(jq -r '.upload.method' <<< "${UPLOAD_SESSION}")" \
--header "Content-Type: $(jq -r '.upload.headers["Content-Type"]' <<< "${UPLOAD_SESSION}")" \
--header "X-Nightjar-Image-Intent: $(jq -r '.upload.headers["X-Nightjar-Image-Intent"]' <<< "${UPLOAD_SESSION}")" \
--data-binary "@${NIGHTJAR_IMAGE_PATH}" \
"$(jq -r '.upload.url' <<< "${UPLOAD_SESSION}")" \
| jq -r '.upload_token')"
SOURCE_ASSET="$(api_curl \
--request POST \
--header 'Content-Type: application/json' \
--header "Idempotency-Key: $(idempotency_key complete-upload)" \
--data "$(jq -cn --arg token "${UPLOAD_TOKEN}" '{upload_token:$token}')" \
"${API_BASE}/uploads/$(jq -r '.id' <<< "${UPLOAD_SESSION}")/complete")"
PRODUCT="$(api_curl \
--request POST \
--header 'Content-Type: application/json' \
--header "Idempotency-Key: $(idempotency_key create-product)" \
--data "$(jq -cn --arg id "$(jq -r '.id' <<< "${SOURCE_ASSET}")" \
'{name:"API quickstart product",asset_ids:[$id],primary_asset_id:$id}')" \
"${API_BASE}/products")"
# This Generation spends one Credit.
GENERATION="$(api_curl \
--request POST \
--header 'Content-Type: application/json' \
--header "Idempotency-Key: $(idempotency_key create-generation)" \
--data "$(jq -cn --arg id "$(jq -r '.id' <<< "${PRODUCT}")" \
'{product_ids:[$id],background:{type:"automatic"},fashion_model:{type:"none"},output_mode:"single_shots",image_count:1,aspect_ratio:"1:1",resolution:"1k",output_format:"jpeg"}')" \
"${API_BASE}/generations/product-photography")"
for _ in $(seq 1 120); do
STATUS="$(jq -r '.status' <<< "${GENERATION}")"
if [[ "${STATUS}" == 'completed' || "${STATUS}" == 'failed' ]]; then
break
fi
sleep 2
GENERATION="$(api_curl \
"${API_BASE}/generations/$(jq -r '.id' <<< "${GENERATION}")")"
done
OUTPUT_ID="$(jq -r \
'[.outputs[] | select(.status == "completed" and .asset != null)][0].asset.id // empty' \
<<< "${GENERATION}")"
if [[ -z "${OUTPUT_ID}" ]]; then
printf 'Generation %s produced no completed output.\n' \
"$(jq -r '.id' <<< "${GENERATION}")" >&2
exit 1
fi
OUTPUT_ASSET="$(api_curl "${API_BASE}/assets/${OUTPUT_ID}")"
jq -cn \
--arg generation_id "$(jq -r '.id' <<< "${GENERATION}")" \
--arg asset_id "$(jq -r '.id' <<< "${OUTPUT_ASSET}")" \
--arg url "$(jq -r '.url' <<< "${OUTPUT_ASSET}")" \
'{generation_id:$generation_id,asset_id:$asset_id,url:$url}'
End cURL example.import { randomUUID } from 'node:crypto';
import { readFile } from 'node:fs/promises';
const apiKey = requiredEnvironmentVariable('NIGHTJAR_API_KEY');
const imagePath = requiredEnvironmentVariable('NIGHTJAR_IMAGE_PATH');
const apiBase = process.env.NIGHTJAR_BASE_URL ?? 'https://api.nightjar.so/v1';
function requiredEnvironmentVariable(name) {
const value = process.env[name];
if (!value) {
throw new Error(`${name} is required.`);
}
return value;
}
function idempotencyKey(prefix) {
return `${prefix}-${randomUUID()}`;
}
async function apiRequest(path, init = {}) {
const headers = new Headers(init.headers);
headers.set('Authorization', `Bearer ${apiKey}`);
const response = await fetch(`${apiBase}${path}`, { ...init, headers });
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
const problem = await response.text();
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${problem}`
);
}
return await response.json();
}
async function postJson(path, body, requestKey) {
return await apiRequest(path, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': requestKey,
},
body: JSON.stringify(body),
});
}
async function waitForGeneration(currentGeneration, attemptsRemaining = 120) {
if (
currentGeneration.status === 'completed' ||
currentGeneration.status === 'failed' ||
attemptsRemaining === 0
) {
return currentGeneration;
}
await new Promise((resolve) => setTimeout(resolve, 2000));
const next = await apiRequest(`/generations/${currentGeneration.id}`);
return waitForGeneration(next, attemptsRemaining - 1);
}
const image = await readFile(imagePath);
const upload = await postJson(
'/uploads',
{ content_type: 'image/jpeg', size_bytes: image.byteLength },
idempotencyKey('upload')
);
const uploadResponse = await fetch(upload.upload.url, {
method: upload.upload.method,
headers: upload.upload.headers,
body: image,
});
if (!uploadResponse.ok) {
throw new Error(`Direct upload failed with HTTP ${uploadResponse.status}.`);
}
const receipt = await uploadResponse.json();
const sourceAsset = await postJson(
`/uploads/${upload.id}/complete`,
receipt,
idempotencyKey('complete-upload')
);
const product = await postJson(
'/products',
{
name: 'API quickstart product',
asset_ids: [sourceAsset.id],
primary_asset_id: sourceAsset.id,
},
idempotencyKey('create-product')
);
// This Generation spends one Credit.
const admittedGeneration = await postJson(
'/generations/product-photography',
{
product_ids: [product.id],
background: { type: 'automatic' },
fashion_model: { type: 'none' },
output_mode: 'single_shots',
image_count: 1,
aspect_ratio: '1:1',
resolution: '1k',
output_format: 'jpeg',
},
idempotencyKey('create-generation')
);
const generation = await waitForGeneration(admittedGeneration);
const outputId = generation.outputs.find(
(candidate) => candidate.status === 'completed'
)?.asset?.id;
if (!outputId) {
throw new Error(`Generation ${generation.id} produced no completed output.`);
}
const outputAsset = await apiRequest(`/assets/${outputId}`);
console.log(
JSON.stringify({
generation_id: generation.id,
asset_id: outputAsset.id,
url: outputAsset.url,
})
);
End JavaScript example.import { randomUUID } from 'node:crypto';
import { readFile } from 'node:fs/promises';
const apiKey = requiredEnvironmentVariable('NIGHTJAR_API_KEY');
const imagePath = requiredEnvironmentVariable('NIGHTJAR_IMAGE_PATH');
const apiBase = process.env.NIGHTJAR_BASE_URL ?? 'https://api.nightjar.so/v1';
function requiredEnvironmentVariable(name: string) {
const value = process.env[name];
if (!value) {
throw new Error(`${name} is required.`);
}
return value;
}
function idempotencyKey(prefix: string) {
return `${prefix}-${randomUUID()}`;
}
async function apiRequest<T>(path: string, init: RequestInit = {}) {
const headers = new Headers(init.headers);
headers.set('Authorization', `Bearer ${apiKey}`);
const response = await fetch(`${apiBase}${path}`, { ...init, headers });
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
const problem = await response.text();
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${problem}`
);
}
return (await response.json()) as T;
}
async function postJson<T>(path: string, body: unknown, requestKey: string) {
return await apiRequest<T>(path, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': requestKey,
},
body: JSON.stringify(body),
});
}
async function waitForGeneration(
currentGeneration: Generation,
attemptsRemaining = 120
): Promise<Generation> {
if (
currentGeneration.status === 'completed' ||
currentGeneration.status === 'failed' ||
attemptsRemaining === 0
) {
return currentGeneration;
}
await new Promise((resolve) => setTimeout(resolve, 2000));
const next = await apiRequest<Generation>(
`/generations/${currentGeneration.id}`
);
return waitForGeneration(next, attemptsRemaining - 1);
}
type UploadSession = {
id: string;
upload: {
method: string;
url: string;
headers: Record<string, string>;
};
};
type Asset = { id: string; url: string };
type Product = { id: string };
type Generation = {
id: string;
status: 'queued' | 'processing' | 'completed' | 'failed';
outputs: Array<{
status: 'pending' | 'processing' | 'completed' | 'failed';
asset: { id: string } | null;
}>;
};
const image = await readFile(imagePath);
const upload = await postJson<UploadSession>(
'/uploads',
{ content_type: 'image/jpeg', size_bytes: image.byteLength },
idempotencyKey('upload')
);
const uploadResponse = await fetch(upload.upload.url, {
method: upload.upload.method,
headers: upload.upload.headers,
body: image,
});
if (!uploadResponse.ok) {
throw new Error(`Direct upload failed with HTTP ${uploadResponse.status}.`);
}
const receipt = (await uploadResponse.json()) as { upload_token: string };
const sourceAsset = await postJson<Asset>(
`/uploads/${upload.id}/complete`,
receipt,
idempotencyKey('complete-upload')
);
const product = await postJson<Product>(
'/products',
{
name: 'API quickstart product',
asset_ids: [sourceAsset.id],
primary_asset_id: sourceAsset.id,
},
idempotencyKey('create-product')
);
// This Generation spends one Credit.
const admittedGeneration = await postJson<Generation>(
'/generations/product-photography',
{
product_ids: [product.id],
background: { type: 'automatic' },
fashion_model: { type: 'none' },
output_mode: 'single_shots',
image_count: 1,
aspect_ratio: '1:1',
resolution: '1k',
output_format: 'jpeg',
},
idempotencyKey('create-generation')
);
const generation = await waitForGeneration(admittedGeneration);
const outputId = generation.outputs.find(
(candidate) => candidate.status === 'completed'
)?.asset?.id;
if (!outputId) {
throw new Error(`Generation ${generation.id} produced no completed output.`);
}
const outputAsset = await apiRequest<Asset>(`/assets/${outputId}`);
console.log(
JSON.stringify({
generation_id: generation.id,
asset_id: outputAsset.id,
url: outputAsset.url,
})
);
End TypeScript example.import json
import os
import time
import urllib.error
import urllib.request
import uuid
def required_environment_variable(name: str) -> str:
value = os.environ.get(name)
if not value:
raise RuntimeError(f"{name} is required.")
return value
API_KEY = required_environment_variable("NIGHTJAR_API_KEY")
IMAGE_PATH = required_environment_variable("NIGHTJAR_IMAGE_PATH")
API_BASE = os.environ.get("NIGHTJAR_BASE_URL", "https://api.nightjar.so/v1")
def api_request(path: str, method: str = "GET", body=None, request_key=None):
headers = {"Authorization": f"Bearer {API_KEY}"}
data = None
if body is not None:
headers["Content-Type"] = "application/json"
data = json.dumps(body).encode("utf-8")
if request_key:
headers["Idempotency-Key"] = request_key
request = urllib.request.Request(
f"{API_BASE}{path}", data=data, headers=headers, method=method
)
try:
with urllib.request.urlopen(request) as response:
return json.load(response)
except urllib.error.HTTPError as error:
request_id = error.headers.get("Request-Id", "unavailable")
problem = error.read().decode("utf-8")
raise RuntimeError(
f"Nightjar request failed ({error.code}, Request-Id {request_id}): {problem}"
) from error
def post_json(path: str, body, prefix: str):
return api_request(
path,
method="POST",
body=body,
request_key=f"{prefix}-{uuid.uuid4()}",
)
with open(IMAGE_PATH, "rb") as image_file:
image = image_file.read()
upload = post_json(
"/uploads",
{"content_type": "image/jpeg", "size_bytes": len(image)},
"upload",
)
direct_request = urllib.request.Request(
upload["upload"]["url"],
data=image,
headers=upload["upload"]["headers"],
method=upload["upload"]["method"],
)
with urllib.request.urlopen(direct_request) as direct_response:
receipt = json.load(direct_response)
source_asset = post_json(
f"/uploads/{upload['id']}/complete", receipt, "complete-upload"
)
product = post_json(
"/products",
{
"name": "API quickstart product",
"asset_ids": [source_asset["id"]],
"primary_asset_id": source_asset["id"],
},
"create-product",
)
# This Generation spends one Credit.
generation = post_json(
"/generations/product-photography",
{
"product_ids": [product["id"]],
"background": {"type": "automatic"},
"fashion_model": {"type": "none"},
"output_mode": "single_shots",
"image_count": 1,
"aspect_ratio": "1:1",
"resolution": "1k",
"output_format": "jpeg",
},
"create-generation",
)
for _ in range(120):
if generation["status"] in ("completed", "failed"):
break
time.sleep(2)
generation = api_request(f"/generations/{generation['id']}")
output_id = next(
(
output["asset"]["id"]
for output in generation["outputs"]
if output["status"] == "completed" and output["asset"]
),
None,
)
if not output_id:
raise RuntimeError(f"Generation {generation['id']} produced no completed output.")
output = api_request(f"/assets/{output_id}")
print(
json.dumps(
{
"generation_id": generation["id"],
"asset_id": output["id"],
"url": output["url"],
}
)
)
End Python example.Mental model
Six durable concepts
Team
Every credential acts as one Team: its Library, Products, Credits, and Subscription.
Asset
A durable image identity from an upload or Generation, with a stable public-by-link URL.
Product
A reusable group of one or more Product Photo Assets, with one Primary Product Photo.
Reusable ingredient
A Photography Style, Background, Pose, or Fashion Model, created once and referenced in any Generation.
Operation
Non-cancelable asynchronous authoring work that creates or replaces a reusable ingredient.
Generation
Non-cancelable creative work with stable output slots, Credit reservation, and per-output results.
Files
Upload once, then reference the Asset
- Create one Upload Session with the source content type and exact byte length.
- POST raw bytes to the returned short-lived URL with every returned header. Do not send the API Credential to that URL.
- Exchange the returned upload token at the Session’s complete endpoint.
- Keep the resulting Asset ID and use it in Products, authoring, Edit Images, or Upscale.
JPG, PNG, GIF, WebP, and AVIF sources up to 70 MiB are accepted. Nightjar normalizes the durable Asset to JPEG, PNG, or WebP. A failed or expired Session creates no placeholder Asset.
Delivery
GET /v1/assets/:id. Downloading the URL does not require the API Credential.Library
Create the same resources the web app uses
Assets, Products, Photography Styles, Backgrounds, Poses, and Fashion Models are Team-scoped Library resources. Ingredient lists include usable Team and global resources by default; use scope=team or scope=global to narrow them.
Product Photos are many-to-many: the same Asset can belong to several Products. Product updates replace the declared membership set atomically and keep a deterministic Primary Product Photo.
Which writes return an Operation
Authoring
Poll Operations until they are terminal
A 202 authoring response contains the Operation and a Location header. Poll that resource until completed or failed. Operations cannot be canceled.
while true; do
response="$(curl --fail-with-body \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/operations/${OPERATION_ID}")"
status="$(printf '%s' "${response}" | jq -r .status)"
[[ "${status}" == completed || "${status}" == failed ]] && break
sleep 2
doneEnd cURL example.const operationId = process.env.OPERATION_ID;
let operation;
do {
const response = await fetch(
`https://api.nightjar.so/v1/operations/${operationId}`,
{ headers: { Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}` } }
);
if (!response.ok) throw new Error(await response.text());
operation = await response.json();
if (operation.status !== 'completed' && operation.status !== 'failed') {
await new Promise((resolve) => setTimeout(resolve, 2000));
}
} while (operation.status !== 'completed' && operation.status !== 'failed');End JavaScript example.type OperationStatus = 'queued' | 'processing' | 'completed' | 'failed';
type Operation = { id: string; status: OperationStatus };
const operationId = process.env.OPERATION_ID;
if (!operationId) throw new Error('OPERATION_ID is required.');
let operation: Operation;
do {
const response = await fetch(
`https://api.nightjar.so/v1/operations/${operationId}`,
{ headers: { Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}` } }
);
if (!response.ok) throw new Error(await response.text());
operation = (await response.json()) as Operation;
if (operation.status !== 'completed' && operation.status !== 'failed') {
await new Promise((resolve) => setTimeout(resolve, 2000));
}
} while (operation.status !== 'completed' && operation.status !== 'failed');End TypeScript example.import os
import time
import requests
operation_id = os.environ['OPERATION_ID']
headers = {'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}"}
while True:
response = requests.get(
f'https://api.nightjar.so/v1/operations/{operation_id}',
headers=headers,
timeout=60,
)
response.raise_for_status()
operation = response.json()
if operation['status'] in ('completed', 'failed'):
break
time.sleep(2)End Python example.Creation failure leaves no partial resource. Update failure preserves the previous version. While a source-changing Operation is active, PATCH and DELETE return 409 resource_operation_in_progress with its ID.
Credits follow generated media
Creative work
Stable outputs, even when work partially succeeds
Generation admission returns 202, reserves the complete planned Credit amount, and allocates every output slot. Poll the Generation until it is terminal; replaying the same Idempotency-Key returns the original admission response and never restarts work.
while true; do
response="$(curl --fail-with-body \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/generations/${GENERATION_ID}")"
status="$(printf '%s' "${response}" | jq -r .status)"
[[ "${status}" == completed || "${status}" == failed ]] && break
sleep 2
doneEnd cURL example.const generationId = process.env.GENERATION_ID;
let generation;
do {
const response = await fetch(
`https://api.nightjar.so/v1/generations/${generationId}`,
{ headers: { Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}` } }
);
if (!response.ok) throw new Error(await response.text());
generation = await response.json();
if (generation.status !== 'completed' && generation.status !== 'failed') {
await new Promise((resolve) => setTimeout(resolve, 2000));
}
} while (generation.status !== 'completed' && generation.status !== 'failed');End JavaScript example.type GenerationStatus = 'queued' | 'processing' | 'completed' | 'failed';
type Generation = { id: string; status: GenerationStatus };
const generationId = process.env.GENERATION_ID;
if (!generationId) throw new Error('GENERATION_ID is required.');
let generation: Generation;
do {
const response = await fetch(
`https://api.nightjar.so/v1/generations/${generationId}`,
{ headers: { Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}` } }
);
if (!response.ok) throw new Error(await response.text());
generation = (await response.json()) as Generation;
if (generation.status !== 'completed' && generation.status !== 'failed') {
await new Promise((resolve) => setTimeout(resolve, 2000));
}
} while (generation.status !== 'completed' && generation.status !== 'failed');End TypeScript example.import os
import time
import requests
generation_id = os.environ['GENERATION_ID']
headers = {'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}"}
while True:
response = requests.get(
f'https://api.nightjar.so/v1/generations/{generation_id}',
headers=headers,
timeout=60,
)
response.raise_for_status()
generation = response.json()
if generation['status'] in ('completed', 'failed'):
break
time.sleep(2)End Python example.A Generation is terminal only when every output is terminal. If at least one output succeeds, the Generation is completed; failed slots retain provider-neutral errors and successful slots retain Asset references. If every slot fails, the Generation is failed. Only completed outputs are charged.
No cancellation in v1
Creative surface
Three workflows, one lifecycle
Product Photography
Use Product IDs, loose Asset IDs, or both. output_mode is required: single_shots produces 1–6 independent images; photoshoot produces one cohesive four-image set.
Edit Images
Use one to eight Assets and natural-language instructions to produce exactly one edited output. All inputs jointly guide the result.
Upscale
Upscale one Asset to 2K or 4K. An Asset already at or above the requested target is rejected synchronously and costs nothing.
Resilience
Design retries around explicit contracts
Idempotency
Pagination
Limits
Deletion
Problem details
Machine codes stay stable; details stay human
Errors use application/problem+json. Branch on code, not title or detail. request_id matches the Request-Id response header.
| HTTP | Code | Meaning | Recovery |
|---|---|---|---|
| 400 | invalid_request | Malformed JSON, unsupported query parameters, or a missing required header. | Correct the request before retrying. |
| 400 | invalid_cursor | The pagination cursor is invalid for this collection or filter set. | Restart pagination without the cursor. |
| 401 | authentication_required | The API Credential is missing or invalid. | Supply a valid Bearer credential. |
| 401 | credential_expired | The API Credential reached its configured expiry. | Create a replacement credential in Settings. |
| 401 | credential_revoked | The API Credential was revoked. | Use a different active credential. |
| 402 | insufficient_credits | The Team cannot reserve enough Credits for this work. | Add Credits, then submit a new logical request. |
| 403 | permission_denied | The credential profile cannot perform this operation. | Use a full credential for writes. |
| 403 | subscription_required | Creative writes require an active Nightjar Subscription. | Restore the Team Subscription; reads remain available. |
| 404 | not_found | The resource does not exist or is not visible to this Team. | Treat the resource as unavailable. |
| 404 | upload_not_found | The Upload Session is unavailable. | Create a new Upload Session. |
| 404 | asset_not_found | The Asset is unavailable to this Team. | Use a live Asset from the Team Library. |
| 404 | product_not_found | The Product is unavailable to this Team. | Use a live Product from the Team Library. |
| 409 | idempotency_in_progress | The same Idempotency-Key is still being admitted. | Wait, then replay the same key and intent. |
| 409 | resource_operation_in_progress | The resource already has source-changing authoring work in progress. | Poll active_operation_id before retrying. |
| 409 | upload_bytes_conflict | Different bytes were sent to an Upload Session that already accepted bytes. | Create a new Upload Session for the different file. |
| 410 | upload_expired | The direct-upload window expired. | Create a new Upload Session. |
| 422 | invalid_input | A field or field combination violates the documented request contract. | Correct the reported fields before retrying. |
| 422 | invalid_reference | A referenced Asset, Product, or ingredient is not usable by this Team. | Replace the invalid reference. |
| 422 | idempotency_key_reused | The key was already used for a different normalized request intent. | Use the original intent or a new key. |
| 422 | already_at_target_resolution | The Asset already meets or exceeds the requested Upscale target. | Use the Asset as-is or request a higher target. |
| 429 | concurrency_limit_reached | The Team reached its public-API creative-work concurrency budget. | Honor Retry-After and wait for existing work to finish. |
| 429 | rate_limited | The request rate exceeded the per-credential or Team limit. | Honor Retry-After and retry with backoff. |
| 503 | service_unavailable | Nightjar temporarily paused new API write admission. | Honor Retry-After. Reads and already accepted work remain available. |
| 500 | internal_error | Nightjar could not complete the HTTP request. | Retry transient failures and include Request-Id if support is needed. |
Stability
A small compatibility promise you can plan around
Nightjar versions breaking changes in the path. Additive endpoints and optional fields may ship inside /v1; removals, renames, changed meanings, or new required inputs require a new major version. Clients must ignore unknown response fields.
24-month major-version support
Deprecated behavior is announced in the changelog and migration guide, then marked with the standard Deprecation header and a Link with rel="deprecation". Planned removal also includes the standard Sunset HTTP-date header.
Changelog
Published the initial v1 contract: 38 operations for uploads, Library resources, authoring Operations, and all three Generation workflows.
Generated from OpenAPI
Complete endpoint reference
Every operation below, including its language examples, response statuses, and Credit label, is derived from the downloadable canonical contract.
Team
Team Credits and API capabilities.
get/v1/teamRetrieve the credential's Team
/v1/teamRetrieve the credential's TeamParameters
No parameters.
200401429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/team"End cURL example.const response = await fetch('https://api.nightjar.so/v1/team', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/team', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/team',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200The credential, its Team, Credits, and effective API capabilities.
{
"object": "team",
"id": "string",
"name": "string",
"credits": 0,
"credential": {
"object": "api_credential",
"id": "string",
"name": "string",
"prefix": "string",
"profile": "read_only",
"expires_at": "2026-08-16T10:00:00.000Z"
},
"capabilities": {
"api_access": false,
"subscription_active": false,
"creative_writes_allowed": false,
"api_concurrency": {
"limit": 0,
"used": 0
}
}
}Uploads
Direct-upload session creation and finalization.
post/v1/uploadsCreate an Upload Session
/v1/uploadsCreate an Upload SessionParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Idempotency-Key | header | string | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| content_type | body | image/jpeg | image/png | image/gif | image/webp | image/avif | Yes | Accepted source formats match the web app. Nightjar normalizes the stored Asset to jpeg, png, or webp. |
| size_bytes | body | integer | Yes | Raw source byte length. The maximum is 70 MiB. |
201400401403409422429503defaultRequest
curl --fail-with-body \
--request POST \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"content_type":"image/jpeg","size_bytes":184320}' \
"https://api.nightjar.so/v1/uploads"End cURL example.const response = await fetch('https://api.nightjar.so/v1/uploads', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"content_type": "image/jpeg",
"size_bytes": 184320
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/uploads', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"content_type": "image/jpeg",
"size_bytes": 184320
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
'Content-Type': 'application/json',
}
payload = {
"content_type": "image/jpeg",
"size_bytes": 184320,
}
response = requests.request(
'POST',
'https://api.nightjar.so/v1/uploads',
headers=headers,
json=payload,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
201One short-lived Upload Session. No Asset exists yet.
{
"object": "upload",
"id": "string",
"status": "awaiting_upload",
"upload": {
"method": "POST",
"url": "https://example.com/resource",
"headers": {},
"expires_at": "2026-08-16T10:00:00.000Z",
"response": {
"content_type": "application/json",
"body": {
"upload_token": "string"
}
}
}
}post/v1/uploads/{upload_id}/completeFinalize an Upload Session into an Asset
/v1/uploads/{upload_id}/completeFinalize an Upload Session into an AssetParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| upload_id | path | string | Yes | — |
| Idempotency-Key | header | string | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| upload_token | body | string | Yes | — |
201400401403404409410422429503defaultRequest
curl --fail-with-body \
--request POST \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"upload_token":"upl_token_from_direct_upload"}' \
"https://api.nightjar.so/v1/uploads/upl_example/complete"End cURL example.const response = await fetch('https://api.nightjar.so/v1/uploads/upl_example/complete', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"upload_token": "upl_token_from_direct_upload"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/uploads/upl_example/complete', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"upload_token": "upl_token_from_direct_upload"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
'Content-Type': 'application/json',
}
payload = {
"upload_token": "upl_token_from_direct_upload",
}
response = requests.request(
'POST',
'https://api.nightjar.so/v1/uploads/upl_example/complete',
headers=headers,
json=payload,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
201The complete usable Asset created by finalization.
{
"object": "asset",
"id": "ast_example",
"media_type": "image",
"source": "upload",
"source_generation_id": null,
"format": "jpeg",
"width": 1200,
"height": 800,
"size_bytes": 184320,
"url": "https://assets.nightjar.so/team/example.jpg",
"created_at": "2026-08-16T10:00:00.000Z"
}Assets
Durable uploaded and generated images.
get/v1/assetsList Team Assets
/v1/assetsList Team AssetsParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | No | — |
| after | query | string | No | — |
| source | query | upload | generation | No | — |
200400401403429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/assets"End cURL example.const response = await fetch('https://api.nightjar.so/v1/assets', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/assets', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/assets',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Team Assets ordered by created_at desc, id desc.
{
"data": [
{
"object": "asset",
"id": "ast_example",
"media_type": "image",
"source": "upload",
"source_generation_id": null,
"format": "jpeg",
"width": 1200,
"height": 800,
"size_bytes": 184320,
"url": "https://assets.nightjar.so/team/example.jpg",
"created_at": "2026-08-16T10:00:00.000Z"
}
],
"page": {
"has_more": false,
"next_cursor": "string"
}
}get/v1/assets/{asset_id}Retrieve an Asset
/v1/assets/{asset_id}Retrieve an AssetParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| asset_id | path | string | Yes | — |
200401403404429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/assets/ast_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/assets/ast_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/assets/ast_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/assets/ast_example',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200One active Asset.
{
"object": "asset",
"id": "ast_example",
"media_type": "image",
"source": "upload",
"source_generation_id": null,
"format": "jpeg",
"width": 1200,
"height": 800,
"size_bytes": 184320,
"url": "https://assets.nightjar.so/team/example.jpg",
"created_at": "2026-08-16T10:00:00.000Z"
}delete/v1/assets/{asset_id}Delete an Asset
/v1/assets/{asset_id}Delete an AssetParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| asset_id | path | string | Yes | — |
200401403404409429503defaultRequest
curl --fail-with-body \
--request DELETE \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/assets/ast_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/assets/ast_example', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/assets/ast_example', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'DELETE',
'https://api.nightjar.so/v1/assets/ast_example',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Deleted from authenticated API and UI use. Every Product Photo membership is removed; affected nonempty Products receive a deterministic replacement Primary Product Photo, and only Products left empty are deleted. Historical Generation output references remain and may resolve to 404.
{
"object": "asset_deletion",
"id": "ast_example",
"removed_product_memberships": 2,
"reassigned_primary_product_ids": [
"prd_surviving"
],
"deleted_product_ids": [
"prd_left_empty"
]
}Products
Reusable collections of Product Photos.
get/v1/productsList Products
/v1/productsList ProductsParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | No | — |
| after | query | string | No | — |
200400401403429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/products"End cURL example.const response = await fetch('https://api.nightjar.so/v1/products', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/products', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/products',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Team Products ordered by created_at desc, id desc.
{
"data": [
{
"object": "product",
"id": "prd_example",
"name": "Field Bag",
"description": "Waxed canvas with leather handles",
"dimensions": "40 × 28 × 12 cm",
"asset_ids": [
"ast_example_front",
"ast_example_detail"
],
"primary_asset_id": "ast_example_front",
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z"
}
],
"page": {
"has_more": false,
"next_cursor": "string"
}
}post/v1/productsCreate a Product
/v1/productsCreate a ProductParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Idempotency-Key | header | string | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| name | body | string | Yes | — |
| description | body | string | No | — |
| dimensions | body | string | No | — |
| asset_ids | body | string[] | Yes | — |
| primary_asset_id | body | string | Yes | Must be present in asset_ids. |
201401403409422429503defaultRequest
curl --fail-with-body \
--request POST \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"name":"Field Bag","description":"Waxed canvas with leather handles","dimensions":"40 × 28 × 12 cm","asset_ids":["ast_example_front","ast_example_detail"],"primary_asset_id":"ast_example_front"}' \
"https://api.nightjar.so/v1/products"End cURL example.const response = await fetch('https://api.nightjar.so/v1/products', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Field Bag",
"description": "Waxed canvas with leather handles",
"dimensions": "40 × 28 × 12 cm",
"asset_ids": [
"ast_example_front",
"ast_example_detail"
],
"primary_asset_id": "ast_example_front"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/products', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Field Bag",
"description": "Waxed canvas with leather handles",
"dimensions": "40 × 28 × 12 cm",
"asset_ids": [
"ast_example_front",
"ast_example_detail"
],
"primary_asset_id": "ast_example_front"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
'Content-Type': 'application/json',
}
payload = {
"name": "Field Bag",
"description": "Waxed canvas with leather handles",
"dimensions": "40 × 28 × 12 cm",
"asset_ids": [
"ast_example_front",
"ast_example_detail",
],
"primary_asset_id": "ast_example_front",
}
response = requests.request(
'POST',
'https://api.nightjar.so/v1/products',
headers=headers,
json=payload,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
201Product created synchronously and atomically.
{
"object": "product",
"id": "prd_example",
"name": "Field Bag",
"description": "Waxed canvas with leather handles",
"dimensions": "40 × 28 × 12 cm",
"asset_ids": [
"ast_example_front",
"ast_example_detail"
],
"primary_asset_id": "ast_example_front",
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z"
}get/v1/products/{product_id}Retrieve a Product
/v1/products/{product_id}Retrieve a ProductParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| product_id | path | string | Yes | — |
200401403404429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/products/prd_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/products/prd_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/products/prd_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/products/prd_example',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200One Product.
{
"object": "product",
"id": "prd_example",
"name": "Field Bag",
"description": "Waxed canvas with leather handles",
"dimensions": "40 × 28 × 12 cm",
"asset_ids": [
"ast_example_front",
"ast_example_detail"
],
"primary_asset_id": "ast_example_front",
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z"
}patch/v1/products/{product_id}Update a Product
/v1/products/{product_id}Update a ProductParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| product_id | path | string | Yes | — |
| Idempotency-Key | header | string | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| name | body | string | No | — |
| description | body | string | No | — |
| dimensions | body | string | No | — |
| asset_ids | body | string[] | No | — |
| primary_asset_id | body | string | No | — |
200401403404409422429503defaultRequest
curl --fail-with-body \
--request PATCH \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"name":"Field Bag · Navy","asset_ids":["ast_example_front","ast_example_navy"],"primary_asset_id":"ast_example_navy"}' \
"https://api.nightjar.so/v1/products/prd_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/products/prd_example', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Field Bag · Navy",
"asset_ids": [
"ast_example_front",
"ast_example_navy"
],
"primary_asset_id": "ast_example_navy"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/products/prd_example', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Field Bag · Navy",
"asset_ids": [
"ast_example_front",
"ast_example_navy"
],
"primary_asset_id": "ast_example_navy"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
'Content-Type': 'application/json',
}
payload = {
"name": "Field Bag · Navy",
"asset_ids": [
"ast_example_front",
"ast_example_navy",
],
"primary_asset_id": "ast_example_navy",
}
response = requests.request(
'PATCH',
'https://api.nightjar.so/v1/products/prd_example',
headers=headers,
json=payload,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Updated synchronously and atomically.
{
"object": "product",
"id": "prd_example",
"name": "Field Bag",
"description": "Waxed canvas with leather handles",
"dimensions": "40 × 28 × 12 cm",
"asset_ids": [
"ast_example_front",
"ast_example_detail"
],
"primary_asset_id": "ast_example_front",
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z"
}delete/v1/products/{product_id}Delete a Product
/v1/products/{product_id}Delete a ProductParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| product_id | path | string | Yes | — |
204401403404409429503defaultRequest
curl --fail-with-body \
--request DELETE \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/products/prd_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/products/prd_example', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/products/prd_example', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'DELETE',
'https://api.nightjar.so/v1/products/prd_example',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
204Permanently removed from API and UI use. Product Photo relationships dissolve; Assets and historical Product Attribution remain.
No response body.
Photography Styles
Reusable visual style ingredients.
get/v1/photography-stylesList Photography Styles
/v1/photography-stylesList Photography StylesReturns every usable Team-owned and global resource by default, ordered together by created_at desc, id desc. Use scope to narrow the collection.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | No | — |
| after | query | string | No | — |
| scope | query | all | team | global | No | Omit for every resource the Team can use, or filter to Team-owned or global premade resources. |
200400401403429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/photography-styles"End cURL example.const response = await fetch('https://api.nightjar.so/v1/photography-styles', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/photography-styles', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/photography-styles',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Team and premade Photography Styles.
{
"data": [
{
"object": "photography_style",
"id": null,
"scope": null,
"name": null,
"source_asset_ids": null,
"created_at": null,
"updated_at": null,
"description": "string",
"preview_image_urls": []
}
],
"page": {
"has_more": false,
"next_cursor": "string"
}
}post/v1/photography-stylesCreate a Photography StyleFree
/v1/photography-stylesCreate a Photography StyleFreeCreates a Photography Style asynchronously from exactly three Team Assets. No image is generated, so it costs zero Credits.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Idempotency-Key | header | string | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| name | body | string | Yes | — |
| description | body | string | No | — |
| asset_ids | body | string[] | Yes | — |
202401403409422429503defaultRequest
curl --fail-with-body \
--request POST \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"name":"Quiet Editorial","description":"Soft daylight and restrained warm neutrals.","asset_ids":["ast_style_one","ast_style_two","ast_style_three"]}' \
"https://api.nightjar.so/v1/photography-styles"End cURL example.const response = await fetch('https://api.nightjar.so/v1/photography-styles', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Quiet Editorial",
"description": "Soft daylight and restrained warm neutrals.",
"asset_ids": [
"ast_style_one",
"ast_style_two",
"ast_style_three"
]
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/photography-styles', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Quiet Editorial",
"description": "Soft daylight and restrained warm neutrals.",
"asset_ids": [
"ast_style_one",
"ast_style_two",
"ast_style_three"
]
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
'Content-Type': 'application/json',
}
payload = {
"name": "Quiet Editorial",
"description": "Soft daylight and restrained warm neutrals.",
"asset_ids": [
"ast_style_one",
"ast_style_two",
"ast_style_three",
],
}
response = requests.request(
'POST',
'https://api.nightjar.so/v1/photography-styles',
headers=headers,
json=payload,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
202Non-cancelable authoring work accepted.
{
"object": "operation",
"id": "string",
"type": "string",
"status": "queued",
"request": {},
"target": {
"object": "photography_style",
"id": "string"
},
"result": {
"object": "photography_style",
"id": "string"
},
"error": {
"code": null,
"message": null,
"retryable": null
},
"credits": {
"reserved": 0,
"charged": 0
},
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"started_at": "2026-08-16T10:00:00.000Z",
"finished_at": "2026-08-16T10:00:00.000Z"
}get/v1/photography-styles/{photography_style_id}Retrieve a Photography Style
/v1/photography-styles/{photography_style_id}Retrieve a Photography StyleParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| photography_style_id | path | string | Yes | — |
200401403404429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/photography-styles/sty_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/photography-styles/sty_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/photography-styles/sty_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/photography-styles/sty_example',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200One Photography Style.
{
"object": "photography_style",
"id": "string",
"scope": "team",
"name": "string",
"source_asset_ids": [
"string"
],
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"description": "string",
"preview_image_urls": [
"https://example.com/resource"
]
}patch/v1/photography-styles/{photography_style_id}Update a Photography StyleFree
/v1/photography-styles/{photography_style_id}Update a Photography StyleFreeMetadata updates and source replacement are free because no image is generated.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| photography_style_id | path | string | Yes | — |
| Idempotency-Key | header | string | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| name | body | string | No | — |
| description | body | string | No | — |
| asset_ids | body | string[] | No | — |
200202401403404409422429503defaultRequest
curl --fail-with-body \
--request PATCH \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"name":"Quiet Editorial · Spring"}' \
"https://api.nightjar.so/v1/photography-styles/sty_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/photography-styles/sty_example', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Quiet Editorial · Spring"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/photography-styles/sty_example', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Quiet Editorial · Spring"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
'Content-Type': 'application/json',
}
payload = {
"name": "Quiet Editorial · Spring",
}
response = requests.request(
'PATCH',
'https://api.nightjar.so/v1/photography-styles/sty_example',
headers=headers,
json=payload,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Returned only for a metadata-only patch, which commits synchronously and free.
{
"object": "photography_style",
"id": "string",
"scope": "team",
"name": "string",
"source_asset_ids": [
"string"
],
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"description": "string",
"preview_image_urls": [
"https://example.com/resource"
]
}delete/v1/photography-styles/{photography_style_id}Delete a Photography Style
/v1/photography-styles/{photography_style_id}Delete a Photography StyleParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| photography_style_id | path | string | Yes | — |
204401403404409429503defaultRequest
curl --fail-with-body \
--request DELETE \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/photography-styles/sty_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/photography-styles/sty_example', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/photography-styles/sty_example', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'DELETE',
'https://api.nightjar.so/v1/photography-styles/sty_example',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
204Permanently removed from API and UI use.
No response body.
Backgrounds
Reusable background ingredients.
get/v1/backgroundsList Backgrounds
/v1/backgroundsList BackgroundsReturns every usable Team-owned and global resource by default, ordered together by created_at desc, id desc. Use scope to narrow the collection.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | No | — |
| after | query | string | No | — |
| scope | query | all | team | global | No | Omit for every resource the Team can use, or filter to Team-owned or global premade resources. |
200400401403429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/backgrounds"End cURL example.const response = await fetch('https://api.nightjar.so/v1/backgrounds', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/backgrounds', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/backgrounds',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Team and premade Backgrounds.
{
"data": [
{
"object": "background",
"id": null,
"scope": null,
"name": null,
"source_asset_ids": null,
"created_at": null,
"updated_at": null,
"description": "string",
"kind": "backdrop",
"card_image_url": "https://example.com/resource"
}
],
"page": {
"has_more": false,
"next_cursor": "string"
}
}post/v1/backgroundsCreate a Background0 or 1 Credit
/v1/backgroundsCreate a Background0 or 1 CreditCreates a Background from one Team Asset. It is free when the scene is already clean and costs exactly one Credit only when foreground-removal image generation runs and commits.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Idempotency-Key | header | string | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| name | body | string | Yes | — |
| kind | body | backdrop | location | Yes | — |
| asset_id | body | string | Yes | — |
202401402403409422429503defaultRequest
curl --fail-with-body \
--request POST \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"name":"Warm Stone Studio","kind":"backdrop","asset_id":"ast_background_source"}' \
"https://api.nightjar.so/v1/backgrounds"End cURL example.const response = await fetch('https://api.nightjar.so/v1/backgrounds', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Warm Stone Studio",
"kind": "backdrop",
"asset_id": "ast_background_source"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/backgrounds', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Warm Stone Studio",
"kind": "backdrop",
"asset_id": "ast_background_source"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
'Content-Type': 'application/json',
}
payload = {
"name": "Warm Stone Studio",
"kind": "backdrop",
"asset_id": "ast_background_source",
}
response = requests.request(
'POST',
'https://api.nightjar.so/v1/backgrounds',
headers=headers,
json=payload,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
202Non-cancelable authoring work accepted.
{
"object": "operation",
"id": "string",
"type": "string",
"status": "queued",
"request": {},
"target": {
"object": "photography_style",
"id": "string"
},
"result": {
"object": "photography_style",
"id": "string"
},
"error": {
"code": null,
"message": null,
"retryable": null
},
"credits": {
"reserved": 0,
"charged": 0
},
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"started_at": "2026-08-16T10:00:00.000Z",
"finished_at": "2026-08-16T10:00:00.000Z"
}get/v1/backgrounds/{background_id}Retrieve a Background
/v1/backgrounds/{background_id}Retrieve a BackgroundParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| background_id | path | string | Yes | — |
200401403404429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/backgrounds/bkg_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/backgrounds/bkg_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/backgrounds/bkg_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/backgrounds/bkg_example',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200One Background.
{
"object": "background",
"id": "string",
"scope": "team",
"name": "string",
"source_asset_ids": [
"string"
],
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"description": "string",
"kind": "backdrop",
"card_image_url": "https://example.com/resource"
}patch/v1/backgrounds/{background_id}Update a Background0 or 1 Credit
/v1/backgrounds/{background_id}Update a Background0 or 1 CreditMetadata updates are free. Replacing the source costs one Credit only when foreground-removal image generation runs and commits.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| background_id | path | string | Yes | — |
| Idempotency-Key | header | string | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| name | body | string | No | — |
| kind | body | backdrop | location | No | — |
| asset_id | body | string | No | — |
200202401402403404409422429503defaultRequest
curl --fail-with-body \
--request PATCH \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"name":"Warm Stone Studio · Clean"}' \
"https://api.nightjar.so/v1/backgrounds/bkg_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/backgrounds/bkg_example', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Warm Stone Studio · Clean"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/backgrounds/bkg_example', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Warm Stone Studio · Clean"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
'Content-Type': 'application/json',
}
payload = {
"name": "Warm Stone Studio · Clean",
}
response = requests.request(
'PATCH',
'https://api.nightjar.so/v1/backgrounds/bkg_example',
headers=headers,
json=payload,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Returned only for a metadata-only patch, which commits synchronously and free.
{
"object": "background",
"id": "string",
"scope": "team",
"name": "string",
"source_asset_ids": [
"string"
],
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"description": "string",
"kind": "backdrop",
"card_image_url": "https://example.com/resource"
}delete/v1/backgrounds/{background_id}Delete a Background
/v1/backgrounds/{background_id}Delete a BackgroundParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| background_id | path | string | Yes | — |
204401403404409429503defaultRequest
curl --fail-with-body \
--request DELETE \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/backgrounds/bkg_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/backgrounds/bkg_example', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/backgrounds/bkg_example', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'DELETE',
'https://api.nightjar.so/v1/backgrounds/bkg_example',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
204Permanently removed from API and UI use.
No response body.
Poses
Reusable pose ingredients.
get/v1/posesList Poses
/v1/posesList PosesReturns every usable Team-owned and global resource by default, ordered together by created_at desc, id desc. Use scope to narrow the collection.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | No | — |
| after | query | string | No | — |
| scope | query | all | team | global | No | Omit for every resource the Team can use, or filter to Team-owned or global premade resources. |
200400401403429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/poses"End cURL example.const response = await fetch('https://api.nightjar.so/v1/poses', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/poses', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/poses',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Team and premade Poses.
{
"data": [
{
"object": "pose",
"id": null,
"scope": null,
"name": null,
"source_asset_ids": null,
"created_at": null,
"updated_at": null,
"description": "string",
"card_image_url": "https://example.com/resource",
"supported_camera_distances": []
}
],
"page": {
"has_more": false,
"next_cursor": "string"
}
}post/v1/posesCreate a Pose1 Credit
/v1/posesCreate a Pose1 CreditCreates a Pose from one Team Asset. A new neutral card image is generated, so a successful commit costs exactly one Credit.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Idempotency-Key | header | string | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| name | body | string | Yes | — |
| asset_id | body | string | Yes | — |
| supported_camera_distances | body | close | medium | far[] | No | — |
202401402403409422429503defaultRequest
curl --fail-with-body \
--request POST \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"name":"Relaxed Standing","asset_id":"ast_pose_source"}' \
"https://api.nightjar.so/v1/poses"End cURL example.const response = await fetch('https://api.nightjar.so/v1/poses', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Relaxed Standing",
"asset_id": "ast_pose_source"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/poses', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Relaxed Standing",
"asset_id": "ast_pose_source"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
'Content-Type': 'application/json',
}
payload = {
"name": "Relaxed Standing",
"asset_id": "ast_pose_source",
}
response = requests.request(
'POST',
'https://api.nightjar.so/v1/poses',
headers=headers,
json=payload,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
202Non-cancelable authoring work accepted.
{
"object": "operation",
"id": "string",
"type": "string",
"status": "queued",
"request": {},
"target": {
"object": "photography_style",
"id": "string"
},
"result": {
"object": "photography_style",
"id": "string"
},
"error": {
"code": null,
"message": null,
"retryable": null
},
"credits": {
"reserved": 0,
"charged": 0
},
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"started_at": "2026-08-16T10:00:00.000Z",
"finished_at": "2026-08-16T10:00:00.000Z"
}get/v1/poses/{pose_id}Retrieve a Pose
/v1/poses/{pose_id}Retrieve a PoseParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| pose_id | path | string | Yes | — |
200401403404429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/poses/pos_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/poses/pos_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/poses/pos_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/poses/pos_example',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200One Pose.
{
"object": "pose",
"id": "string",
"scope": "team",
"name": "string",
"source_asset_ids": [
"string"
],
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"description": "string",
"card_image_url": "https://example.com/resource",
"supported_camera_distances": [
"close"
]
}patch/v1/poses/{pose_id}Update a Pose0 or 1 Credit
/v1/poses/{pose_id}Update a Pose0 or 1 CreditMetadata updates are free. Replacing the source generates a new card and costs one Credit on successful commit.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| pose_id | path | string | Yes | — |
| Idempotency-Key | header | string | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| name | body | string | No | — |
| asset_id | body | string | No | — |
| supported_camera_distances | body | close | medium | far[] | No | — |
200202401402403404409422429503defaultRequest
curl --fail-with-body \
--request PATCH \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"name":"Relaxed Standing · Front"}' \
"https://api.nightjar.so/v1/poses/pos_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/poses/pos_example', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Relaxed Standing · Front"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/poses/pos_example', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Relaxed Standing · Front"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
'Content-Type': 'application/json',
}
payload = {
"name": "Relaxed Standing · Front",
}
response = requests.request(
'PATCH',
'https://api.nightjar.so/v1/poses/pos_example',
headers=headers,
json=payload,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Returned only for a metadata-only patch, which commits synchronously and free.
{
"object": "pose",
"id": "string",
"scope": "team",
"name": "string",
"source_asset_ids": [
"string"
],
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"description": "string",
"card_image_url": "https://example.com/resource",
"supported_camera_distances": [
"close"
]
}delete/v1/poses/{pose_id}Delete a Pose
/v1/poses/{pose_id}Delete a PoseParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| pose_id | path | string | Yes | — |
204401403404409429503defaultRequest
curl --fail-with-body \
--request DELETE \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/poses/pos_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/poses/pos_example', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/poses/pos_example', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'DELETE',
'https://api.nightjar.so/v1/poses/pos_example',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
204Permanently removed from API and UI use.
No response body.
Fashion Models
Reusable fashion model ingredients.
get/v1/fashion-modelsList Fashion Models
/v1/fashion-modelsList Fashion ModelsReturns every usable Team-owned and global resource by default, ordered together by created_at desc, id desc. Use scope to narrow the collection.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | No | — |
| after | query | string | No | — |
| scope | query | all | team | global | No | Omit for every resource the Team can use, or filter to Team-owned or global premade resources. |
200400401403429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/fashion-models"End cURL example.const response = await fetch('https://api.nightjar.so/v1/fashion-models', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/fashion-models', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/fashion-models',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Team and premade Fashion Models.
{
"data": [
{
"object": "fashion_model",
"id": null,
"scope": null,
"name": null,
"source_asset_ids": null,
"created_at": null,
"updated_at": null,
"age_range": null,
"gender": null,
"card_image_url": "https://example.com/resource"
}
],
"page": {
"has_more": false,
"next_cursor": "string"
}
}post/v1/fashion-modelsCreate a Fashion Model1 Credit
/v1/fashion-modelsCreate a Fashion Model1 CreditCreates a Fashion Model from one to five Team Assets and required metadata. A new identity card is generated, so a successful commit costs exactly one Credit.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Idempotency-Key | header | string | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| name | body | string | Yes | — |
| age_range | body | 18-25 | 25-35 | 35-45 | 45-55 | 55+ | Yes | — |
| gender | body | male | female | neutral | Yes | — |
| asset_ids | body | string[] | Yes | — |
202401402403409422429503defaultRequest
curl --fail-with-body \
--request POST \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"name":"Alex","age_range":"25-35","gender":"female","asset_ids":["ast_model_front","ast_model_profile"]}' \
"https://api.nightjar.so/v1/fashion-models"End cURL example.const response = await fetch('https://api.nightjar.so/v1/fashion-models', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Alex",
"age_range": "25-35",
"gender": "female",
"asset_ids": [
"ast_model_front",
"ast_model_profile"
]
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/fashion-models', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Alex",
"age_range": "25-35",
"gender": "female",
"asset_ids": [
"ast_model_front",
"ast_model_profile"
]
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
'Content-Type': 'application/json',
}
payload = {
"name": "Alex",
"age_range": "25-35",
"gender": "female",
"asset_ids": [
"ast_model_front",
"ast_model_profile",
],
}
response = requests.request(
'POST',
'https://api.nightjar.so/v1/fashion-models',
headers=headers,
json=payload,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
202Non-cancelable authoring work accepted.
{
"object": "operation",
"id": "string",
"type": "string",
"status": "queued",
"request": {},
"target": {
"object": "photography_style",
"id": "string"
},
"result": {
"object": "photography_style",
"id": "string"
},
"error": {
"code": null,
"message": null,
"retryable": null
},
"credits": {
"reserved": 0,
"charged": 0
},
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"started_at": "2026-08-16T10:00:00.000Z",
"finished_at": "2026-08-16T10:00:00.000Z"
}get/v1/fashion-models/{fashion_model_id}Retrieve a Fashion Model
/v1/fashion-models/{fashion_model_id}Retrieve a Fashion ModelParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| fashion_model_id | path | string | Yes | — |
200401403404429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/fashion-models/mdl_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/fashion-models/mdl_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/fashion-models/mdl_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/fashion-models/mdl_example',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200One Fashion Model.
{
"object": "fashion_model",
"id": "string",
"scope": "team",
"name": "string",
"source_asset_ids": [
"string"
],
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"age_range": "18-25",
"gender": "male",
"card_image_url": "https://example.com/resource"
}patch/v1/fashion-models/{fashion_model_id}Update a Fashion Model0 or 1 Credit
/v1/fashion-models/{fashion_model_id}Update a Fashion Model0 or 1 CreditMetadata updates are free. Replacing source Assets generates a new identity card and costs one Credit on successful commit.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| fashion_model_id | path | string | Yes | — |
| Idempotency-Key | header | string | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| name | body | string | No | — |
| age_range | body | 18-25 | 25-35 | 35-45 | 45-55 | 55+ | No | — |
| gender | body | male | female | neutral | No | — |
| asset_ids | body | string[] | No | — |
200202401402403404409422429503defaultRequest
curl --fail-with-body \
--request PATCH \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"name":"Alex · Summer"}' \
"https://api.nightjar.so/v1/fashion-models/mdl_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/fashion-models/mdl_example', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Alex · Summer"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/fashion-models/mdl_example', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"name": "Alex · Summer"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
'Content-Type': 'application/json',
}
payload = {
"name": "Alex · Summer",
}
response = requests.request(
'PATCH',
'https://api.nightjar.so/v1/fashion-models/mdl_example',
headers=headers,
json=payload,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Returned only for a metadata-only patch, which commits synchronously and free.
{
"object": "fashion_model",
"id": "string",
"scope": "team",
"name": "string",
"source_asset_ids": [
"string"
],
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"age_range": "18-25",
"gender": "male",
"card_image_url": "https://example.com/resource"
}delete/v1/fashion-models/{fashion_model_id}Delete a Fashion Model
/v1/fashion-models/{fashion_model_id}Delete a Fashion ModelParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| fashion_model_id | path | string | Yes | — |
204401403404409429503defaultRequest
curl --fail-with-body \
--request DELETE \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/fashion-models/mdl_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/fashion-models/mdl_example', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/fashion-models/mdl_example', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'DELETE',
'https://api.nightjar.so/v1/fashion-models/mdl_example',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
204Permanently removed from API and UI use.
No response body.
Operations
Asynchronous reusable-ingredient authoring work.
get/v1/operationsList authoring Operations
/v1/operationsList authoring OperationsReturns Team authoring Operations ordered by created_at desc, id desc. Use the filters to recover specific in-flight or historical work.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | No | — |
| after | query | string | No | — |
| status | query | queued | processing | completed | failed | No | — |
| type | query | photography_style.create | photography_style.update | background.create | background.update | pose.create | pose.update | fashion_model.create | fashion_model.update | No | — |
| target_id | query | string | No | — |
200400401403429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/operations"End cURL example.const response = await fetch('https://api.nightjar.so/v1/operations', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/operations', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/operations',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Matching authoring Operations.
{
"data": [
{
"object": "operation",
"id": "string",
"type": "string",
"status": "queued",
"request": {},
"target": null,
"result": null,
"error": null,
"credits": {
"reserved": null,
"charged": null
},
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"started_at": "2026-08-16T10:00:00.000Z",
"finished_at": "2026-08-16T10:00:00.000Z"
}
],
"page": {
"has_more": false,
"next_cursor": "string"
}
}get/v1/operations/{operation_id}Retrieve an authoring Operation
/v1/operations/{operation_id}Retrieve an authoring OperationParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| operation_id | path | string | Yes | — |
200401403404429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/operations/op_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/operations/op_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/operations/op_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/operations/op_example',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Authoritative authoring lifecycle snapshot.
{
"object": "operation",
"id": "string",
"type": "string",
"status": "queued",
"request": {},
"target": {
"object": "photography_style",
"id": "string"
},
"result": {
"object": "photography_style",
"id": "string"
},
"error": {
"code": null,
"message": null,
"retryable": null
},
"credits": {
"reserved": 0,
"charged": 0
},
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"started_at": "2026-08-16T10:00:00.000Z",
"finished_at": "2026-08-16T10:00:00.000Z"
}Generations
Asynchronous creative workflow executions.
get/v1/generationsList Generations
/v1/generationsList GenerationsParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | No | — |
| after | query | string | No | — |
| workflow | query | product_photography | edit_images | upscale | No | — |
| status | query | queued | processing | completed | failed | No | — |
200400401403429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/generations"End cURL example.const response = await fetch('https://api.nightjar.so/v1/generations', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/generations', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/generations',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Public-API Generations for the Team across all of its API Credentials, ordered by created_at desc, id desc. Web-only Generations that cannot satisfy this contract are excluded.
{
"data": [
{}
],
"page": {
"has_more": false,
"next_cursor": "string"
}
}get/v1/generations/{generation_id}Retrieve a Generation
/v1/generations/{generation_id}Retrieve a GenerationParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| generation_id | path | string | Yes | — |
200401403404429defaultRequest
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
"https://api.nightjar.so/v1/generations/gen_example"End cURL example.const response = await fetch('https://api.nightjar.so/v1/generations/gen_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/generations/gen_example', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
},
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
}
response = requests.request(
'GET',
'https://api.nightjar.so/v1/generations/gen_example',
headers=headers,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
200Authoritative Generation lifecycle and output snapshot.
{
"object": "generation",
"id": "string",
"initiated_by": null,
"status": null,
"output_summary": null,
"outputs": [],
"credits": null,
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"started_at": "2026-08-16T10:00:00.000Z",
"finished_at": "2026-08-16T10:00:00.000Z",
"workflow": "product_photography",
"request": null
}post/v1/generations/product-photographyStart Product Photography or a Photoshoot1 Credit per generated image
/v1/generations/product-photographyStart Product Photography or a Photoshoot1 Credit per generated imageEvery selected Product and loose Asset is a joint subject in every output. single_shots costs one Credit per requested 1k or 2k image and two Credits per requested 4k image. photoshoot costs two Credits total and produces four output slots. The complete amount is planned and reserved before admission.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Idempotency-Key | header | string | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
202401402403409422429503defaultRequest
curl --fail-with-body \
--request POST \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"product_ids":["prd_01JPRODUCT"],"asset_ids":["ast_01JLOOSEANGLE"],"photography_style_id":"sty_01JEDITORIAL","background":{"type":"background","background_id":"bkg_01JWARMSTUDIO"},"fashion_model":{"type":"selected","fashion_model_id":"mdl_01JALEX"},"pose_id":"pos_01JSTANDING","camera_distance":"medium","custom_directions":"Keep both products clearly visible.","output_mode":"single_shots","image_count":2,"aspect_ratio":"4:5","resolution":"2k","output_format":"webp"}' \
"https://api.nightjar.so/v1/generations/product-photography"End cURL example.const response = await fetch('https://api.nightjar.so/v1/generations/product-photography', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"product_ids": [
"prd_01JPRODUCT"
],
"asset_ids": [
"ast_01JLOOSEANGLE"
],
"photography_style_id": "sty_01JEDITORIAL",
"background": {
"type": "background",
"background_id": "bkg_01JWARMSTUDIO"
},
"fashion_model": {
"type": "selected",
"fashion_model_id": "mdl_01JALEX"
},
"pose_id": "pos_01JSTANDING",
"camera_distance": "medium",
"custom_directions": "Keep both products clearly visible.",
"output_mode": "single_shots",
"image_count": 2,
"aspect_ratio": "4:5",
"resolution": "2k",
"output_format": "webp"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/generations/product-photography', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"product_ids": [
"prd_01JPRODUCT"
],
"asset_ids": [
"ast_01JLOOSEANGLE"
],
"photography_style_id": "sty_01JEDITORIAL",
"background": {
"type": "background",
"background_id": "bkg_01JWARMSTUDIO"
},
"fashion_model": {
"type": "selected",
"fashion_model_id": "mdl_01JALEX"
},
"pose_id": "pos_01JSTANDING",
"camera_distance": "medium",
"custom_directions": "Keep both products clearly visible.",
"output_mode": "single_shots",
"image_count": 2,
"aspect_ratio": "4:5",
"resolution": "2k",
"output_format": "webp"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
'Content-Type': 'application/json',
}
payload = {
"product_ids": [
"prd_01JPRODUCT",
],
"asset_ids": [
"ast_01JLOOSEANGLE",
],
"photography_style_id": "sty_01JEDITORIAL",
"background": {
"type": "background",
"background_id": "bkg_01JWARMSTUDIO",
},
"fashion_model": {
"type": "selected",
"fashion_model_id": "mdl_01JALEX",
},
"pose_id": "pos_01JSTANDING",
"camera_distance": "medium",
"custom_directions": "Keep both products clearly visible.",
"output_mode": "single_shots",
"image_count": 2,
"aspect_ratio": "4:5",
"resolution": "2k",
"output_format": "webp",
}
response = requests.request(
'POST',
'https://api.nightjar.so/v1/generations/product-photography',
headers=headers,
json=payload,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
202Non-cancelable Generation admitted with stable output slots.
{
"object": "generation",
"id": "string",
"initiated_by": null,
"status": null,
"output_summary": null,
"outputs": [],
"credits": null,
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"started_at": "2026-08-16T10:00:00.000Z",
"finished_at": "2026-08-16T10:00:00.000Z",
"workflow": "product_photography",
"request": null
}post/v1/generations/edit-imagesStart Edit Images1 or 2 Credits
/v1/generations/edit-imagesStart Edit Images1 or 2 CreditsAll one to eight input Assets jointly guide exactly one edited output. Cost is one Credit at 1k or 2k and two Credits at 4k, planned and reserved before admission.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Idempotency-Key | header | string | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| aspect_ratio | body | 21:9 | 1:1 | 4:3 | 3:2 | 2:3 | 5:4 | 4:5 | 3:4 | 16:9 | 9:16 | Yes | — |
| resolution | body | 1k | 2k | 4k | Yes | — |
| output_format | body | jpeg | png | webp | Yes | — |
| asset_ids | body | string[] | Yes | — |
| instructions | body | string | Yes | — |
202401402403409422429503defaultRequest
curl --fail-with-body \
--request POST \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"asset_ids":["ast_01JPRODUCT","ast_01JSCENE"],"instructions":"Place the product from image 1 into the scene from image 2.","aspect_ratio":"4:5","resolution":"2k","output_format":"webp"}' \
"https://api.nightjar.so/v1/generations/edit-images"End cURL example.const response = await fetch('https://api.nightjar.so/v1/generations/edit-images', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"asset_ids": [
"ast_01JPRODUCT",
"ast_01JSCENE"
],
"instructions": "Place the product from image 1 into the scene from image 2.",
"aspect_ratio": "4:5",
"resolution": "2k",
"output_format": "webp"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/generations/edit-images', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"asset_ids": [
"ast_01JPRODUCT",
"ast_01JSCENE"
],
"instructions": "Place the product from image 1 into the scene from image 2.",
"aspect_ratio": "4:5",
"resolution": "2k",
"output_format": "webp"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
'Content-Type': 'application/json',
}
payload = {
"asset_ids": [
"ast_01JPRODUCT",
"ast_01JSCENE",
],
"instructions": "Place the product from image 1 into the scene from image 2.",
"aspect_ratio": "4:5",
"resolution": "2k",
"output_format": "webp",
}
response = requests.request(
'POST',
'https://api.nightjar.so/v1/generations/edit-images',
headers=headers,
json=payload,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
202Non-cancelable Generation admitted with stable output slots.
{
"object": "generation",
"id": "string",
"initiated_by": null,
"status": null,
"output_summary": null,
"outputs": [],
"credits": null,
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"started_at": "2026-08-16T10:00:00.000Z",
"finished_at": "2026-08-16T10:00:00.000Z",
"workflow": "product_photography",
"request": null
}post/v1/generations/upscaleStart Upscale1 or 2 Credits
/v1/generations/upscaleStart Upscale1 or 2 CreditsProduces exactly one upscaled output. Cost is one Credit at 2k and two Credits at 4k, planned and reserved before admission. An Asset already at or above the requested target is rejected synchronously and costs nothing.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Idempotency-Key | header | string | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| asset_id | body | string | Yes | — |
| target_resolution | body | 2k | 4k | Yes | — |
| output_format | body | jpeg | png | webp | Yes | — |
202401402403409422429503defaultRequest
curl --fail-with-body \
--request POST \
--header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"asset_id":"ast_01JPRODUCT","target_resolution":"2k","output_format":"webp"}' \
"https://api.nightjar.so/v1/generations/upscale"End cURL example.const response = await fetch('https://api.nightjar.so/v1/generations/upscale', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"asset_id": "ast_01JPRODUCT",
"target_resolution": "2k",
"output_format": "webp"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result =
response.status === 204 ? null : await response.json();
console.log(result);End JavaScript example.const response = await fetch('https://api.nightjar.so/v1/generations/upscale', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NIGHTJAR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"asset_id": "ast_01JPRODUCT",
"target_resolution": "2k",
"output_format": "webp"
}
),
});
if (!response.ok) {
const requestId = response.headers.get('Request-Id') ?? 'unavailable';
throw new Error(
`Nightjar request failed (${response.status}, Request-Id ${requestId}): ${await response.text()}`
);
}
const result: unknown =
response.status === 204 ? null : await response.json();
console.log(result);End TypeScript example.import os
import requests
headers = {
'Authorization': f"Bearer {os.environ['NIGHTJAR_API_KEY']}",
'Content-Type': 'application/json',
}
payload = {
"asset_id": "ast_01JPRODUCT",
"target_resolution": "2k",
"output_format": "webp",
}
response = requests.request(
'POST',
'https://api.nightjar.so/v1/generations/upscale',
headers=headers,
json=payload,
timeout=60,
)
if not response.ok:
request_id = response.headers.get('Request-Id', 'unavailable')
raise RuntimeError(
f'Nightjar request failed ({response.status_code}, Request-Id {request_id}): {response.text}'
)
print(response.json() if response.content else None)End Python example.Successful response
202Non-cancelable Generation admitted with stable output slots.
{
"object": "generation",
"id": "string",
"initiated_by": null,
"status": null,
"output_summary": null,
"outputs": [],
"credits": null,
"created_at": "2026-08-16T10:00:00.000Z",
"updated_at": "2026-08-16T10:00:00.000Z",
"started_at": "2026-08-16T10:00:00.000Z",
"finished_at": "2026-08-16T10:00:00.000Z",
"workflow": "product_photography",
"request": null
}