Imports

Upload Import Events

Uploads a batch of raw events to an in-progress import.

POST /api/sites/:site/imports/:importId/events

Uploads a batch of raw events to an in-progress import. Requires the editor role or above on the site. On Rybbit Cloud, this requires a paid plan.

Upload events in batches. The request body limit is 50 MB, so split large exports across multiple requests. Set isLastBatch to true on the final batch to finalize the import.

Path Parameters

Prop

Type

Request Body

Prop

Type

Response

Returns 200 OK with an empty body. Progress shows up in the import's counts (see List Imports):

  • 400 for the whole batch if the events don't all fit one platform's shape (a missing key, a non-string value, or rows from different platforms in the same batch). Nothing from that batch is stored.
  • The shape check accepts any of the three platforms before the import's own platform is used. A batch in another platform's shape (for example, Umami rows sent to a Plausible import) returns 200, but every event is dropped and counted in invalidEvents.
  • An event whose values don't validate (bad timestamp, malformed ID, over-long field) is dropped and counted in invalidEvents.
  • An event is skipped and counted in skippedEvents when its timestamp is in the future, is older than your plan's historical window, or falls in a month whose import quota is already used up.

Other errors: 403 on the Rybbit Cloud free plan, 404 if the import does not exist (a site that doesn't exist gets 403), 400 if the import belongs to another site.

Request
curl -X POST "https://app.rybbit.io/api/sites/123/imports/f47ac10b-58cc-4372-a567-0e02b2c3d479/events" \
  -H "Authorization: Bearer your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      { "...": "platform-specific fields" }
    ],
    "isLastBatch": false
  }'
Request
const response = await fetch(
  'https://app.rybbit.io/api/sites/123/imports/f47ac10b-58cc-4372-a567-0e02b2c3d479/events',
  {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer your_api_key_here',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      events: [
        { '...': 'platform-specific fields' }
      ],
      isLastBatch: false
    })
  }
);
Request
import requests

response = requests.post(
    'https://app.rybbit.io/api/sites/123/imports/f47ac10b-58cc-4372-a567-0e02b2c3d479/events',
    json={
        'events': [
            { '...': 'platform-specific fields' }
        ],
        'isLastBatch': False
    },
    headers={
        'Authorization': 'Bearer your_api_key_here'
    }
)
Request
$body = [
    'events' => [
        ['...' => 'platform-specific fields']
    ],
    'isLastBatch' => false
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://app.rybbit.io/api/sites/123/imports/f47ac10b-58cc-4372-a567-0e02b2c3d479/events');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer your_api_key_here',
    'Content-Type: application/json'
]);

$response = curl_exec($ch);
curl_close($ch);
Request
require 'net/http'
require 'json'

uri = URI('https://app.rybbit.io/api/sites/123/imports/f47ac10b-58cc-4372-a567-0e02b2c3d479/events')
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer your_api_key_here'
request['Content-Type'] = 'application/json'
request.body = {
  events: [
    { '...': 'platform-specific fields' }
  ],
  isLastBatch: false
}.to_json

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end
Request
body := map[string]interface{}{
    "events": []map[string]string{
        {"...": "platform-specific fields"},
    },
    "isLastBatch": false,
}
jsonBody, _ := json.Marshal(body)

req, _ := http.NewRequest("POST", "https://app.rybbit.io/api/sites/123/imports/f47ac10b-58cc-4372-a567-0e02b2c3d479/events", bytes.NewBuffer(jsonBody))
req.Header.Set("Authorization", "Bearer your_api_key_here")
req.Header.Set("Content-Type", "application/json")

client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
Request
let body = serde_json::json!({
    "events": [
        {"...": "platform-specific fields"}
    ],
    "isLastBatch": false
});

let client = reqwest::Client::new();
let res = client
    .post("https://app.rybbit.io/api/sites/123/imports/f47ac10b-58cc-4372-a567-0e02b2c3d479/events")
    .header("Authorization", "Bearer your_api_key_here")
    .json(&body)
    .send()
    .await?;
Request
String json = """
{
  "events": [
    {"...": "platform-specific fields"}
  ],
  "isLastBatch": false
}
""";

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://app.rybbit.io/api/sites/123/imports/f47ac10b-58cc-4372-a567-0e02b2c3d479/events"))
    .header("Authorization", "Bearer your_api_key_here")
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(json))
    .build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
Request
var body = new
{
    events = new[]
    {
        new Dictionary<string, string> { { "...", "platform-specific fields" } }
    },
    isLastBatch = false
};

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer your_api_key_here");

var content = new StringContent(
    JsonSerializer.Serialize(body),
    Encoding.UTF8,
    "application/json");

var response = await client.PostAsync("https://app.rybbit.io/api/sites/123/imports/f47ac10b-58cc-4372-a567-0e02b2c3d479/events", content);