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
curl https://upload.ad/api/v1/creatives/cr_1/copy \
  -H "Authorization: Bearer $UPLOADAD_API_KEY"
JavaScript
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();
Python
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
<?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);
Go
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))
}
Ruby
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"]
Java
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());
	}
}
C#
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);
json
{
	"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": "..." }.

FieldTypeDescription
headlinestring, optionalUp to 500 characters
primaryTextstring, optionalUp to 5000 characters
descriptionstring, optionalUp to 1000 characters
labelstring, optionalYour own tag for the variant, up to 100 characters
idstring, optionalInclude to update an existing variant
cURL
# 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" }'
JavaScript
// 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();
Python
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
<?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);
Go
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))
}
Ruby
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"]
Java
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());
	}
}
C#
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
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" }'
JavaScript
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' })
});
Python
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
<?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);
Go
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()
}
Ruby
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) }
Java
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());
	}
}
C#
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());
json
{ "ok": true }