curl --request POST \
--url https://api.creem.io/v1/events/preview \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"events": [
{
"name": "image.generated",
"customer_id": "cust_abc123",
"event_id": "order-1234",
"timestamp": "2026-08-15T10:30:00.000Z",
"properties": {
"tokens": 1200
},
"metadata": {
"tier": "pro"
}
}
]
}
'import requests
url = "https://api.creem.io/v1/events/preview"
payload = { "events": [
{
"name": "image.generated",
"customer_id": "cust_abc123",
"event_id": "order-1234",
"timestamp": "2026-08-15T10:30:00.000Z",
"properties": { "tokens": 1200 },
"metadata": { "tier": "pro" }
}
] }
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
events: [
{
name: 'image.generated',
customer_id: 'cust_abc123',
event_id: 'order-1234',
timestamp: '2026-08-15T10:30:00.000Z',
properties: {tokens: 1200},
metadata: {tier: 'pro'}
}
]
})
};
fetch('https://api.creem.io/v1/events/preview', 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.creem.io/v1/events/preview",
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([
'events' => [
[
'name' => 'image.generated',
'customer_id' => 'cust_abc123',
'event_id' => 'order-1234',
'timestamp' => '2026-08-15T10:30:00.000Z',
'properties' => [
'tokens' => 1200
],
'metadata' => [
'tier' => 'pro'
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$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.creem.io/v1/events/preview"
payload := strings.NewReader("{\n \"events\": [\n {\n \"name\": \"image.generated\",\n \"customer_id\": \"cust_abc123\",\n \"event_id\": \"order-1234\",\n \"timestamp\": \"2026-08-15T10:30:00.000Z\",\n \"properties\": {\n \"tokens\": 1200\n },\n \"metadata\": {\n \"tier\": \"pro\"\n }\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
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.creem.io/v1/events/preview")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"events\": [\n {\n \"name\": \"image.generated\",\n \"customer_id\": \"cust_abc123\",\n \"event_id\": \"order-1234\",\n \"timestamp\": \"2026-08-15T10:30:00.000Z\",\n \"properties\": {\n \"tokens\": 1200\n },\n \"metadata\": {\n \"tier\": \"pro\"\n }\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.creem.io/v1/events/preview")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"events\": [\n {\n \"name\": \"image.generated\",\n \"customer_id\": \"cust_abc123\",\n \"event_id\": \"order-1234\",\n \"timestamp\": \"2026-08-15T10:30:00.000Z\",\n \"properties\": {\n \"tokens\": 1200\n },\n \"metadata\": {\n \"tier\": \"pro\"\n }\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"valid": true,
"events": [
{
"valid": true,
"matched_meters": [
"mtr_abc123"
],
"warnings": [
{
"code": "no_matching_meter",
"message": "<string>"
}
],
"event_id": "order-1234",
"error": {
"code": "invalid_usage_event",
"message": "<string>",
"param": "customer_id"
},
"duplicate": {}
}
]
}{
"error": {
"type": "invalid_request_error",
"code": "meter_name_taken",
"message": "A meter named \"Image generations\" already exists in this store",
"request_id": "req_abc123def456",
"param": "events[3].customer_id",
"details": {
"archived_holder": true,
"archived_holder_id": "mtr_abc123"
}
}
}Preview usage events
Dry-run of POST /v1/events/ingest: send the exact batch you would ingest and get a per-event report of what would happen — nothing is stored.
For each event: whether it passes validation (invalid events are reported in place instead of rejecting the batch, unlike ingest), the event_id it would be recorded under, whether it would deduplicate against an already-stored event or an earlier entry of the same batch (advisory — another writer can land between preview and ingest), the active meters that would consume it, and the same warnings ingest returns.
Usage events are immutable once ingested, so preview is the way to gain first-run confidence: wire your integration against preview, check valid is true and every event matches the meters you expect, then switch the path to ingest.
curl --request POST \
--url https://api.creem.io/v1/events/preview \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"events": [
{
"name": "image.generated",
"customer_id": "cust_abc123",
"event_id": "order-1234",
"timestamp": "2026-08-15T10:30:00.000Z",
"properties": {
"tokens": 1200
},
"metadata": {
"tier": "pro"
}
}
]
}
'import requests
url = "https://api.creem.io/v1/events/preview"
payload = { "events": [
{
"name": "image.generated",
"customer_id": "cust_abc123",
"event_id": "order-1234",
"timestamp": "2026-08-15T10:30:00.000Z",
"properties": { "tokens": 1200 },
"metadata": { "tier": "pro" }
}
] }
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
events: [
{
name: 'image.generated',
customer_id: 'cust_abc123',
event_id: 'order-1234',
timestamp: '2026-08-15T10:30:00.000Z',
properties: {tokens: 1200},
metadata: {tier: 'pro'}
}
]
})
};
fetch('https://api.creem.io/v1/events/preview', 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.creem.io/v1/events/preview",
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([
'events' => [
[
'name' => 'image.generated',
'customer_id' => 'cust_abc123',
'event_id' => 'order-1234',
'timestamp' => '2026-08-15T10:30:00.000Z',
'properties' => [
'tokens' => 1200
],
'metadata' => [
'tier' => 'pro'
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$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.creem.io/v1/events/preview"
payload := strings.NewReader("{\n \"events\": [\n {\n \"name\": \"image.generated\",\n \"customer_id\": \"cust_abc123\",\n \"event_id\": \"order-1234\",\n \"timestamp\": \"2026-08-15T10:30:00.000Z\",\n \"properties\": {\n \"tokens\": 1200\n },\n \"metadata\": {\n \"tier\": \"pro\"\n }\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
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.creem.io/v1/events/preview")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"events\": [\n {\n \"name\": \"image.generated\",\n \"customer_id\": \"cust_abc123\",\n \"event_id\": \"order-1234\",\n \"timestamp\": \"2026-08-15T10:30:00.000Z\",\n \"properties\": {\n \"tokens\": 1200\n },\n \"metadata\": {\n \"tier\": \"pro\"\n }\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.creem.io/v1/events/preview")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"events\": [\n {\n \"name\": \"image.generated\",\n \"customer_id\": \"cust_abc123\",\n \"event_id\": \"order-1234\",\n \"timestamp\": \"2026-08-15T10:30:00.000Z\",\n \"properties\": {\n \"tokens\": 1200\n },\n \"metadata\": {\n \"tier\": \"pro\"\n }\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"valid": true,
"events": [
{
"valid": true,
"matched_meters": [
"mtr_abc123"
],
"warnings": [
{
"code": "no_matching_meter",
"message": "<string>"
}
],
"event_id": "order-1234",
"error": {
"code": "invalid_usage_event",
"message": "<string>",
"param": "customer_id"
},
"duplicate": {}
}
]
}{
"error": {
"type": "invalid_request_error",
"code": "meter_name_taken",
"message": "A meter named \"Image generations\" already exists in this store",
"request_id": "req_abc123def456",
"param": "events[3].customer_id",
"details": {
"archived_holder": true,
"archived_holder_id": "mtr_abc123"
}
}
}Authorizations
API key for authentication. You can find your API key in the Creem dashboard under Settings > API Keys.
Body
The batch of usage events to ingest (1–100). The whole batch is validated before anything is written.
Show child attributes
Show child attributes