Copy variants
A copy variant is a set of ad texts (headline, primary text, description) attached to a creative. All fields are optional, so a variant can hold as little as a single headline.
GET/api/v1/creatives/{id}/copy
List copy variants for a creative, newest first. Returns 404 if the creative does not exist.
curl https://upload.ad/api/v1/creatives/cr_1/copy \
-H "Authorization: Bearer $UPLOADAD_API_KEY"const res = await fetch('https://upload.ad/api/v1/creatives/cr_1/copy', {
headers: { Authorization: `Bearer ${process.env.UPLOADAD_API_KEY}` }
});
const { copies } = await res.json();import os, requests
res = requests.get(
"https://upload.ad/api/v1/creatives/cr_1/copy",
headers={"Authorization": f"Bearer {os.environ['UPLOADAD_API_KEY']}"},
)
copies = res.json()["copies"]<?php
$ch = curl_init('https://upload.ad/api/v1/creatives/cr_1/copy');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer ' . getenv('UPLOADAD_API_KEY')]);
$copies = json_decode(curl_exec($ch), true)['copies'];
curl_close($ch);package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://upload.ad/api/v1/creatives/cr_1/copy", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("UPLOADAD_API_KEY"))
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}require "net/http"
require "json"
uri = URI("https://upload.ad/api/v1/creatives/cr_1/copy")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{ENV['UPLOADAD_API_KEY']}"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
copies = JSON.parse(res.body)["copies"]import java.net.URI;
import java.net.http.*;
public class ListCopy {
public static void main(String[] args) throws Exception {
HttpRequest req = HttpRequest.newBuilder(URI.create("https://upload.ad/api/v1/creatives/cr_1/copy"))
.header("Authorization", "Bearer " + System.getenv("UPLOADAD_API_KEY"))
.build();
HttpResponse<String> res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
}
}using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization",
$"Bearer {Environment.GetEnvironmentVariable("UPLOADAD_API_KEY")}");
var body = await client.GetStringAsync("https://upload.ad/api/v1/creatives/cr_1/copy");
Console.WriteLine(body);{
"copies": [
{
"id": "cp_1",
"headline": "Summer sale",
"primaryText": "Save 20% this week only.",
"description": "Limited time offer",
"label": "variant-a",
"createdAt": "2026-07-09T10:20:00.000Z"
}
]
}POST/api/v1/creatives/{id}/copy
Create a copy variant, or update an existing one by including its id in the body. Creation returns 201; both return { "id": "..." }.
| Field | Type | Description |
|---|---|---|
headline | string, optional | Up to 500 characters |
primaryText | string, optional | Up to 5000 characters |
description | string, optional | Up to 1000 characters |
label | string, optional | Your own tag for the variant, up to 100 characters |
id | string, optional | Include to update an existing variant |
# Create
curl -X POST https://upload.ad/api/v1/creatives/cr_1/copy \
-H "Authorization: Bearer $UPLOADAD_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "headline": "Summer sale", "primaryText": "Save 20% this week only.", "label": "variant-a" }'
# Update
curl -X POST https://upload.ad/api/v1/creatives/cr_1/copy \
-H "Authorization: Bearer $UPLOADAD_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "id": "cp_1", "headline": "Summer sale, extended" }'// Add "id" to the body to update an existing variant instead of creating one.
const res = await fetch('https://upload.ad/api/v1/creatives/cr_1/copy', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.UPLOADAD_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
headline: 'Summer sale',
primaryText: 'Save 20% this week only.',
label: 'variant-a'
})
});
const { id } = await res.json();import os, requests
# Add "id" to the body to update an existing variant instead of creating one.
res = requests.post(
"https://upload.ad/api/v1/creatives/cr_1/copy",
headers={"Authorization": f"Bearer {os.environ['UPLOADAD_API_KEY']}"},
json={
"headline": "Summer sale",
"primaryText": "Save 20% this week only.",
"label": "variant-a",
},
)
copy_id = res.json()["id"]<?php
// Add "id" to the body to update an existing variant instead of creating one.
$ch = curl_init('https://upload.ad/api/v1/creatives/cr_1/copy');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . getenv('UPLOADAD_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'headline' => 'Summer sale',
'primaryText' => 'Save 20% this week only.',
'label' => 'variant-a',
]));
$id = json_decode(curl_exec($ch), true)['id'];
curl_close($ch);package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
)
func main() {
// Add "id" to the body to update an existing variant instead of creating one.
body := strings.NewReader(`{ "headline": "Summer sale", "primaryText": "Save 20% this week only.", "label": "variant-a" }`)
req, _ := http.NewRequest("POST", "https://upload.ad/api/v1/creatives/cr_1/copy", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("UPLOADAD_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(string(out))
}require "net/http"
require "json"
# Add "id" to the body to update an existing variant instead of creating one.
uri = URI("https://upload.ad/api/v1/creatives/cr_1/copy")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV['UPLOADAD_API_KEY']}"
req["Content-Type"] = "application/json"
req.body = { headline: "Summer sale", primaryText: "Save 20% this week only.", label: "variant-a" }.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
id = JSON.parse(res.body)["id"]import java.net.URI;
import java.net.http.*;
public class CreateCopy {
public static void main(String[] args) throws Exception {
// Add "id" to the body to update an existing variant instead of creating one.
String body = "{ \"headline\": \"Summer sale\", \"primaryText\": \"Save 20% this week only.\", \"label\": \"variant-a\" }";
HttpRequest req = HttpRequest.newBuilder(URI.create("https://upload.ad/api/v1/creatives/cr_1/copy"))
.header("Authorization", "Bearer " + System.getenv("UPLOADAD_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
}
}using System.Text;
// Add "id" to the body to update an existing variant instead of creating one.
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization",
$"Bearer {Environment.GetEnvironmentVariable("UPLOADAD_API_KEY")}");
var content = new StringContent(
"{ \"headline\": \"Summer sale\", \"primaryText\": \"Save 20% this week only.\", \"label\": \"variant-a\" }",
Encoding.UTF8, "application/json");
var res = await client.PostAsync("https://upload.ad/api/v1/creatives/cr_1/copy", content);
Console.WriteLine(await res.Content.ReadAsStringAsync());DELETE/api/v1/creatives/{id}/copy
Delete a copy variant by id.
curl -X DELETE https://upload.ad/api/v1/creatives/cr_1/copy \
-H "Authorization: Bearer $UPLOADAD_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "id": "cp_1" }'await fetch('https://upload.ad/api/v1/creatives/cr_1/copy', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.UPLOADAD_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ id: 'cp_1' })
});import os, requests
requests.delete(
"https://upload.ad/api/v1/creatives/cr_1/copy",
headers={"Authorization": f"Bearer {os.environ['UPLOADAD_API_KEY']}"},
json={"id": "cp_1"},
)<?php
$ch = curl_init('https://upload.ad/api/v1/creatives/cr_1/copy');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . getenv('UPLOADAD_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['id' => 'cp_1']));
curl_exec($ch);
curl_close($ch);package main
import (
"net/http"
"os"
"strings"
)
func main() {
body := strings.NewReader(`{ "id": "cp_1" }`)
req, _ := http.NewRequest("DELETE", "https://upload.ad/api/v1/creatives/cr_1/copy", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("UPLOADAD_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
res.Body.Close()
}require "net/http"
require "json"
uri = URI("https://upload.ad/api/v1/creatives/cr_1/copy")
req = Net::HTTP::Delete.new(uri)
req["Authorization"] = "Bearer #{ENV['UPLOADAD_API_KEY']}"
req["Content-Type"] = "application/json"
req.body = { id: "cp_1" }.to_json
Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }import java.net.URI;
import java.net.http.*;
public class DeleteCopy {
public static void main(String[] args) throws Exception {
HttpRequest req = HttpRequest.newBuilder(URI.create("https://upload.ad/api/v1/creatives/cr_1/copy"))
.header("Authorization", "Bearer " + System.getenv("UPLOADAD_API_KEY"))
.header("Content-Type", "application/json")
.method("DELETE", HttpRequest.BodyPublishers.ofString("{ \"id\": \"cp_1\" }"))
.build();
HttpResponse<String> res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
}
}using System.Text;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization",
$"Bearer {Environment.GetEnvironmentVariable("UPLOADAD_API_KEY")}");
var req = new HttpRequestMessage(HttpMethod.Delete, "https://upload.ad/api/v1/creatives/cr_1/copy")
{
Content = new StringContent("{ \"id\": \"cp_1\" }", Encoding.UTF8, "application/json")
};
var res = await client.SendAsync(req);
Console.WriteLine(await res.Content.ReadAsStringAsync());{ "ok": true }