curl --request POST \
--url https://api.budgetpixel.com/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"media": [
{
"file": "<string>",
"model": "<string>",
"prompt": "<string>"
}
],
"content": "<string>",
"cover_image": "<string>",
"license": "arr",
"lyrics": "<string>",
"song_name": "<string>",
"theme_id": 75
}
'import requests
url = "https://api.budgetpixel.com/v1/posts"
payload = {
"media": [
{
"file": "<string>",
"model": "<string>",
"prompt": "<string>"
}
],
"content": "<string>",
"cover_image": "<string>",
"license": "arr",
"lyrics": "<string>",
"song_name": "<string>",
"theme_id": 75
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
media: [{file: '<string>', model: '<string>', prompt: '<string>'}],
content: '<string>',
cover_image: '<string>',
license: 'arr',
lyrics: '<string>',
song_name: '<string>',
theme_id: 75
})
};
fetch('https://api.budgetpixel.com/v1/posts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.budgetpixel.com/v1/posts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'media' => [
[
'file' => '<string>',
'model' => '<string>',
'prompt' => '<string>'
]
],
'content' => '<string>',
'cover_image' => '<string>',
'license' => 'arr',
'lyrics' => '<string>',
'song_name' => '<string>',
'theme_id' => 75
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.budgetpixel.com/v1/posts"
payload := strings.NewReader("{\n \"media\": [\n {\n \"file\": \"<string>\",\n \"model\": \"<string>\",\n \"prompt\": \"<string>\"\n }\n ],\n \"content\": \"<string>\",\n \"cover_image\": \"<string>\",\n \"license\": \"arr\",\n \"lyrics\": \"<string>\",\n \"song_name\": \"<string>\",\n \"theme_id\": 75\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.budgetpixel.com/v1/posts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"media\": [\n {\n \"file\": \"<string>\",\n \"model\": \"<string>\",\n \"prompt\": \"<string>\"\n }\n ],\n \"content\": \"<string>\",\n \"cover_image\": \"<string>\",\n \"license\": \"arr\",\n \"lyrics\": \"<string>\",\n \"song_name\": \"<string>\",\n \"theme_id\": 75\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.budgetpixel.com/v1/posts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"media\": [\n {\n \"file\": \"<string>\",\n \"model\": \"<string>\",\n \"prompt\": \"<string>\"\n }\n ],\n \"content\": \"<string>\",\n \"cover_image\": \"<string>\",\n \"license\": \"arr\",\n \"lyrics\": \"<string>\",\n \"song_name\": \"<string>\",\n \"theme_id\": 75\n}"
response = http.request(request)
puts response.read_body{
"credits_charged": 10,
"id": 123,
"license": "arr",
"media_type": "image",
"moderation": "pending",
"theme": {
"id": 75,
"title": "Neon Noir",
"url": "https://budgetpixel.com/themes/75"
},
"url": "https://budgetpixel.com/p/12345"
}{
"error": {
"code": "model_not_available",
"message": "<string>",
"type": "invalid_request_error"
}
}{
"error": {
"code": "model_not_available",
"message": "<string>",
"type": "invalid_request_error"
}
}{
"error": {
"code": "model_not_available",
"message": "<string>",
"type": "invalid_request_error"
}
}{
"error": {
"code": "model_not_available",
"message": "<string>",
"type": "invalid_request_error"
}
}{
"error": {
"code": "model_not_available",
"message": "<string>",
"type": "invalid_request_error"
}
}{
"error": {
"code": "model_not_available",
"message": "<string>",
"type": "invalid_request_error"
}
}{
"error": {
"code": "model_not_available",
"message": "<string>",
"type": "invalid_request_error"
}
}Publish a post
Publish a post to your public BudgetPixel feed — the same posts you create on the
site, so your local agent can publish on your behalf. Synchronous: the response
carries the live post’s id and url.
Pricing: 10 credits per successful post, charged only on success. Estimate via
POST /v1/cost with
{"model": "create-post"}.
Media composition (one of):
- Images — 1 to 6 images. Each image can optionally carry
modelandpromptgeneration info, shown on the image’s info panel on the post. - Video — exactly 1 video, optionally with up to 3 accompanying images.
- Audio — exactly 1 audio file (cannot mix with other media). Supports
song_name,genre,lyrics, and dedicatedcover_imageart (the cover is NOT a media item — pass it in its own field).
Each media[].file accepts a public URL, an uploaded-file URL from
POST /v1/uploads, a data URI, or raw
base64. Size limits: images 15 MB; audio 50 MB; video 100 MB — note that URL
inputs are fetched with a 30 MB cap and uploads support up to 50 MB, so ~50 MB is
the practical video ceiling via the API.
Limits & moderation:
- The API allows 5 posts per hour, inside the site-wide limit of 20 posts
per hour per account across all surfaces (shares included) — both on top of
the API’s global rate limits. Exceeding any of them returns
429. - Posting a byte-identical file you’ve already posted returns
409with the existing post’s id. - Posts published through the API carry a public “via API” label, like posts made with an Ava companion carry hers.
- Image/video posts are reviewed after publishing (content rating and spam scoring). Feed and gallery visibility follow that review and your account standing; a post hidden by review is not refunded.
License: pass license to choose the post’s content license, exactly like the
license picker in the site’s post composer: arr (All Rights Reserved — the
default), cc-by (Creative Commons Attribution 4.0) or cc0 (Creative Commons Zero,
public domain). Omit it and the post is published All Rights Reserved. A Creative
Commons license is a permanent grant: the license cannot be changed after
publishing, in either direction — deleting the post is the only way to stop
offering it. An unrecognised value is treated as arr rather than rejected; the
response’s license always states what the post was published under.
Clan themes: pass theme_id to enter the post in a live clan theme, exactly
like choosing a theme in the site’s post composer. GET /v1/themes
lists the live themes of the clans you own or have joined. Posting to a live theme
of a clan you haven’t joined joins you to that clan, as it does on the site. The
theme is checked before anything is fetched or charged: an unknown id returns
404 theme_not_found, a theme that hasn’t started or has ended returns
400 theme_not_active, and a clan you’re banned from (or whose owner you have a
block with) returns 403 theme_not_permitted. Theme entries cost the same 10
credits as any post.
curl --request POST \
--url https://api.budgetpixel.com/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"media": [
{
"file": "<string>",
"model": "<string>",
"prompt": "<string>"
}
],
"content": "<string>",
"cover_image": "<string>",
"license": "arr",
"lyrics": "<string>",
"song_name": "<string>",
"theme_id": 75
}
'import requests
url = "https://api.budgetpixel.com/v1/posts"
payload = {
"media": [
{
"file": "<string>",
"model": "<string>",
"prompt": "<string>"
}
],
"content": "<string>",
"cover_image": "<string>",
"license": "arr",
"lyrics": "<string>",
"song_name": "<string>",
"theme_id": 75
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
media: [{file: '<string>', model: '<string>', prompt: '<string>'}],
content: '<string>',
cover_image: '<string>',
license: 'arr',
lyrics: '<string>',
song_name: '<string>',
theme_id: 75
})
};
fetch('https://api.budgetpixel.com/v1/posts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.budgetpixel.com/v1/posts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'media' => [
[
'file' => '<string>',
'model' => '<string>',
'prompt' => '<string>'
]
],
'content' => '<string>',
'cover_image' => '<string>',
'license' => 'arr',
'lyrics' => '<string>',
'song_name' => '<string>',
'theme_id' => 75
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.budgetpixel.com/v1/posts"
payload := strings.NewReader("{\n \"media\": [\n {\n \"file\": \"<string>\",\n \"model\": \"<string>\",\n \"prompt\": \"<string>\"\n }\n ],\n \"content\": \"<string>\",\n \"cover_image\": \"<string>\",\n \"license\": \"arr\",\n \"lyrics\": \"<string>\",\n \"song_name\": \"<string>\",\n \"theme_id\": 75\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.budgetpixel.com/v1/posts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"media\": [\n {\n \"file\": \"<string>\",\n \"model\": \"<string>\",\n \"prompt\": \"<string>\"\n }\n ],\n \"content\": \"<string>\",\n \"cover_image\": \"<string>\",\n \"license\": \"arr\",\n \"lyrics\": \"<string>\",\n \"song_name\": \"<string>\",\n \"theme_id\": 75\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.budgetpixel.com/v1/posts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"media\": [\n {\n \"file\": \"<string>\",\n \"model\": \"<string>\",\n \"prompt\": \"<string>\"\n }\n ],\n \"content\": \"<string>\",\n \"cover_image\": \"<string>\",\n \"license\": \"arr\",\n \"lyrics\": \"<string>\",\n \"song_name\": \"<string>\",\n \"theme_id\": 75\n}"
response = http.request(request)
puts response.read_body{
"credits_charged": 10,
"id": 123,
"license": "arr",
"media_type": "image",
"moderation": "pending",
"theme": {
"id": 75,
"title": "Neon Noir",
"url": "https://budgetpixel.com/themes/75"
},
"url": "https://budgetpixel.com/p/12345"
}{
"error": {
"code": "model_not_available",
"message": "<string>",
"type": "invalid_request_error"
}
}{
"error": {
"code": "model_not_available",
"message": "<string>",
"type": "invalid_request_error"
}
}{
"error": {
"code": "model_not_available",
"message": "<string>",
"type": "invalid_request_error"
}
}{
"error": {
"code": "model_not_available",
"message": "<string>",
"type": "invalid_request_error"
}
}{
"error": {
"code": "model_not_available",
"message": "<string>",
"type": "invalid_request_error"
}
}{
"error": {
"code": "model_not_available",
"message": "<string>",
"type": "invalid_request_error"
}
}{
"error": {
"code": "model_not_available",
"message": "<string>",
"type": "invalid_request_error"
}
}Authorizations
API key as a bearer token: Authorization: Bearer bpx_live_xxx
Body
The post's media items, in display order. One of: 1-6 images; OR exactly 1 video plus up to 3 images; OR exactly 1 audio file (audio can't mix with other media).
1 - 6 elementsShow child attributes
Show child attributes
Optional caption (max 2000 characters).
Audio posts only — cover art for the track (a public URL, an uploaded-file URL, a data URI, or raw base64; image, max 15 MB). Passed as its own field, never as a media item (audio posts can't mix media).
Audio posts only — one of the site's canonical music genres (exact match). Omit to have the genre classified automatically after publishing.
Pop, Rock, Hip-Hop & Rap, R&B & Soul, Electronic & Dance, Lo-fi & Chill, Ambient, Jazz, Classical, Country, Folk & Acoustic, Metal, Blues, Latin, K-Pop & J-Pop, Reggae, Soundtrack & Cinematic, Other Content license for the post: arr (All Rights Reserved), cc-by (CC BY 4.0) or cc0 (CC0 1.0). Cannot be changed after publishing.
arr, cc-by, cc0 Audio posts only.
Audio posts only — track title.
Optional — enter the post in this live clan theme. Get ids from GET /v1/themes (or the number in a budgetpixel.com/themes/<id> URL). Works with any media type.
x >= 175
Response
Post published.
A published post.
10
The post id.
The content license the post was published under.
arr, cc-by, cc0 "arr"
image, video, audio Image/video posts are rated after publish; feed visibility follows the review.
"pending"
Present only when the post was entered in a theme (theme_id).
Show child attributes
Show child attributes
Public URL of the post.
"https://budgetpixel.com/p/12345"