Skip to main content

Overview

Ingest data into Tinybird datasources using the ingest() and ingestBatch() methods. These methods support single-row and batch ingestion with full type safety when using the typed client.

Single Row Ingestion

With TinybirdClient

import { createClient } from '@tinybirdco/sdk';

const client = createClient({
  baseUrl: 'https://api.tinybird.co',
  token: process.env.TINYBIRD_TOKEN,
});

await client.ingest('events', {
  timestamp: '2024-01-15 10:30:00',
  event_type: 'page_view',
  user_id: 'user_123',
});

With Typed Tinybird Client

For full type safety with autocomplete:
import { tinybird } from '@tinybird/client';

// Fully typed - TypeScript will validate the event structure
await tinybird.pageViews.ingest({
  timestamp: '2024-01-15 10:30:00',
  pathname: '/home',
  session_id: 'abc123',
  country: 'US',
});

Batch Ingestion

For better performance when ingesting multiple rows, use ingestBatch():
await client.ingestBatch('events', [
  {
    timestamp: '2024-01-15 10:30:00',
    event_type: 'page_view',
    user_id: 'user_123',
  },
  {
    timestamp: '2024-01-15 10:31:00',
    event_type: 'button_click',
    user_id: 'user_456',
  },
  {
    timestamp: '2024-01-15 10:32:00',
    event_type: 'form_submit',
    user_id: 'user_789',
  },
]);

Method Signatures

ingest()

client.ingest<T extends Record<string, unknown>>(
  datasourceName: string,
  event: T,
  options?: IngestOptions
): Promise<IngestResult>

ingestBatch()

client.ingestBatch<T extends Record<string, unknown>>(
  datasourceName: string,
  events: T[],
  options?: IngestOptions
): Promise<IngestResult>

Parameters

datasourceName
string
required
Name of the datasource to ingest data into
event
T
Event object to ingest (for ingest())
events
T[]
Array of event objects to ingest (for ingestBatch())
options
IngestOptions
Additional request options

IngestOptions

timeout
number
Request timeout in milliseconds. Overrides the default client timeout.
signal
AbortSignal
AbortController signal for request cancellation
wait
boolean
Wait for the ingestion to complete before returning. When false, the request returns immediately after the data is received.Default: false

Response

Returns an IngestResult object:
interface IngestResult {
  successful_rows: number;  // Number of rows successfully ingested
  quarantined_rows: number; // Number of rows that failed to ingest
}

Examples

Basic Ingestion

const result = await client.ingest('events', {
  timestamp: '2024-01-15 10:30:00',
  event_type: 'page_view',
  pathname: '/home',
});

console.log(`Ingested ${result.successful_rows} rows`);
if (result.quarantined_rows > 0) {
  console.error(`${result.quarantined_rows} rows failed`);
}

Batch Ingestion

const events = [
  { timestamp: '2024-01-15 10:30:00', event_type: 'page_view' },
  { timestamp: '2024-01-15 10:31:00', event_type: 'button_click' },
  { timestamp: '2024-01-15 10:32:00', event_type: 'form_submit' },
];

const result = await client.ingestBatch('events', events);
console.log(`Ingested ${result.successful_rows}/${events.length} rows`);

With Type Parameter

interface PageViewEvent {
  timestamp: string;
  pathname: string;
  session_id: string;
  country: string | null;
}

await client.ingest<PageViewEvent>('page_views', {
  timestamp: '2024-01-15 10:30:00',
  pathname: '/pricing',
  session_id: 'session_123',
  country: 'US',
});

With Wait Option

Wait for ingestion to complete before returning:
const result = await client.ingest(
  'events',
  {
    timestamp: '2024-01-15 10:30:00',
    event_type: 'page_view',
  },
  { wait: true }
);

// Data is guaranteed to be queryable now
const queryResult = await client.query('recent_events');

With Timeout

await client.ingestBatch(
  'events',
  largeEventArray,
  { timeout: 60000 } // 60 seconds
);

With Cancellation

const controller = new AbortController();

setTimeout(() => controller.abort(), 5000);

try {
  await client.ingestBatch('events', largeEventArray, {
    signal: controller.signal,
  });
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('Ingestion was cancelled');
  }
}

Using Datasources Namespace

The client.datasources namespace also provides an ingest() method:
await client.datasources.ingest('events', {
  timestamp: '2024-01-15 10:30:00',
  event_type: 'page_view',
});

Handling Quarantined Rows

const result = await client.ingestBatch('events', events);

if (result.quarantined_rows > 0) {
  console.error(
    `Warning: ${result.quarantined_rows} rows were quarantined.\n` +
    `Check the Tinybird UI for quarantine details.`
  );
}
Quarantined rows are typically caused by:
  • Schema mismatches (wrong column types)
  • Missing required columns
  • Invalid data formats
  • Constraint violations

Performance Tips

Batch your ingestion: Use ingestBatch() instead of multiple ingest() calls for better performance. Batch sizes of 1,000-10,000 rows are typically optimal.
Avoid waiting: Unless you need to query the data immediately, don’t use wait: true. Tinybird processes ingestion asynchronously for better performance.
Use proper timestamps: Always use ISO 8601 format for DateTime fields: '2024-01-15 10:30:00' or '2024-01-15T10:30:00Z'

Error Handling

import { TinybirdError } from '@tinybirdco/sdk';

try {
  await client.ingest('events', eventData);
} catch (error) {
  if (error instanceof TinybirdError) {
    console.error(`Tinybird API error: ${error.message}`);
    
    if (error.isAuthError()) {
      console.error('Authentication failed - check your token');
    } else if (error.isRateLimitError()) {
      console.error('Rate limit exceeded - retry later');
    } else if (error.isNotFoundError()) {
      console.error('Datasource not found');
    }
  } else {
    throw error;
  }
}

TinybirdClient

Core client class documentation

Query

Query pipe endpoints

Datasource Operations

Append, replace, delete, and truncate

Define Datasource

Define datasource schemas

Build docs developers (and LLMs) love