add() to append records to an existing table. It does not compare incoming
keys with stored keys. Choose merge insert when the
same record may arrive again and should update an existing row.
Append a first batch
Install the Python SDK withpip install lancedb. This complete example starts
with Alice, appends Bob and Charlie, and prints all three records.
append_rows.py
INSERT, including INSERT ... SELECT.
Prepare the input
Match the table’s schema, including required fields and vector dimensions. Supported input formats differ by SDK; see Create a table. Inspect conversion errors before retrying, and verify the final row count and representative values.Choose input for a large load
Avoid building one large in-memory list when records already live in files or arrive as batches. Create a table with the intended schema, then append through the appropriate input path.Read from files
Python accepts apyarrow.dataset.Dataset. This example writes a small Parquet
file locally, creates an empty table with its schema, and appends the two records.
For your own data, point the dataset reader at the input files instead.
load_parquet.py
Produce batches during ingestion
Use batches when processing the input as it arrives. The Python example creates an empty table and appends ten records produced by a batch iterator.Control write parallelism
Python’sadd() can estimate parallelism for materialized inputs. A streaming
reader may not expose its total size or support replay. Use write_parallelism
when controlling the number of concurrent partitions; increasing it can also
increase memory use. TypeScript does not expose that manual option.
Measure with realistic row sizes and storage. Avoid one write per row when a
batch will do, and avoid a large worker count for a small input.
Track completion and progress
Python, TypeScript and Rust support progress callbacks when adding data. These examples start with Alice, append Bob and Charlie, and print the number of rows written as progress updates arrive. After the write succeeds, they query the table to show all three records. Each program uses a temporary local database and removes it when finished.- Python: install
lancedb, save the code asinsert_progress.py, and runpython insert_progress.py. - TypeScript: install
@lancedb/lancedb, save the code asinsert_progress.mts, and runnode insert_progress.mtswith Node.js 22.18 or later. - Rust: download the Rust examples,
extract the project, and run
cargo run --locked --bin progress.
tqdm and pass progress=True instead
of a callback.
Read progress updates
Updates include row and byte counts, elapsed time, and parallel write activity. The field names differ by language:
Python and TypeScript report elapsed time in seconds; Rust returns a
Duration.
The expected row count may be unknown for streaming input. On the final update,
it falls back to the number of rows written.
Keep callbacks short and use the write operation’s result to determine success.
The final done update means progress reporting has ended; it can also occur
after a write fails or is cancelled. Input errors before writing starts may
produce no updates.
TypeScript delivers callbacks asynchronously. Slow callbacks can queue up,
and the add() promise may resolve before every callback has run. Exceptions
from a TypeScript callback are logged and do not abort the write.
Verify writes and handle retries
Wait for the write operation’s result, then check the expected records. Progress callbacks describe activity; they do not establish that a write succeeded. If a request fails after the server may have committed it, blindly repeating an append can duplicate rows. The client’s automatic retries can do the same: on an Enterprise table, the Python and TypeScript 0.40.0 clients resend an append after a retryable status such as504, even when the server had already
committed it, and add() still returns success. Reconcile using your record
IDs, or choose an upsert policy designed for replay.
Configure large uploads
Enterprise This section applies only to LanceDB Enterprise tables (db:// connections). LanceDB Embedded writes are unaffected.
When you write to a LanceDB Enterprise table, the client sends the write as one HTTP insert request unless it splits the data into more than one partition, which it does for a large input it can measure or when you set write_parallelism. A partitioned write goes up as a multipart write: the client cuts each partition into one or more insert parts and uploads them under a single upload id. Each part is streamed. Its size and duration still affect resource use and
timeouts: it has to finish within the client read timeout while the server writes it to object storage, so on very large ingests an oversized part can run past that timeout and surface as:
A part is cut after the input batch that reaches either limit, so cuts fall between batches. Lower the byte budget, with smaller input batches, when large ingests hit the read timeout; lower the duration when uploads are slow or throttled and the byte budget isn’t the limiting factor.
add() after a timeout.
Complete write workflow
Complete examples: create, append, update, upsert and delete
Complete examples: create, append, update, upsert and delete
Each program creates a temporary local database, performs the five operations,
prints the result, and removes its temporary data when finished.After the operations, Alice has 10 logins, Bobby has 21, and Charlie has 6.
Dana is selected for deletion and is absent from the final result.
- Python: install
lancedb, save the code asingestion.py, and runpython ingestion.py. - TypeScript: install
@lancedb/lancedb, save the code asingestion.mts, and runnode ingestion.mtswith Node.js 22.18 or later. - Rust: download the complete Rust project,
extract it, and run
cargo run --locked. It includessrc/main.rs, the Cargo manifest and a lockfile for the example’s dependencies. The code is shown below.