Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/duckdb/duckdb/llms.txt

Use this file to discover all available pages before exploring further.

The DuckDB C API provides a low-level interface for embedding DuckDB in C applications. It follows a simple design with opaque handles and explicit resource management.

Basic Usage Pattern

The typical workflow for using the C API:
1

Open a database

Create or open a DuckDB database file:
duckdb_database db;
duckdb_state state = duckdb_open("my_database.db", &db);
if (state == DuckDBError) {
    // Handle error
}
Pass NULL for an in-memory database.
2

Create a connection

Establish a connection to the database:
duckdb_connection con;
state = duckdb_connect(db, &con);
if (state == DuckDBError) {
    // Handle error
}
3

Execute queries

Run SQL queries and process results:
duckdb_result result;
state = duckdb_query(con, "SELECT * FROM my_table", &result);
if (state == DuckDBSuccess) {
    // Process result
    duckdb_destroy_result(&result);
}
4

Clean up resources

Always free resources in reverse order:
duckdb_disconnect(&con);
duckdb_close(&db);

Core Types

The C API uses opaque handle types:
  • duckdb_database - Database instance handle
  • duckdb_connection - Connection handle
  • duckdb_result - Query result set
  • duckdb_prepared_statement - Prepared statement handle
  • duckdb_appender - Bulk data appender handle

Return Values

Most functions return duckdb_state:
typedef enum duckdb_state {
    DuckDBSuccess = 0,
    DuckDBError = 1
} duckdb_state;
Always check return values and handle errors appropriately.

Complete Example

Here’s a complete C program using the DuckDB API:
#include "duckdb.h"
#include <stdio.h>

int main() {
    duckdb_database db = NULL;
    duckdb_connection con = NULL;
    duckdb_result result;

    // Open in-memory database
    if (duckdb_open(NULL, &db) == DuckDBError) {
        fprintf(stderr, "Failed to open database\n");
        return 1;
    }

    // Create connection
    if (duckdb_connect(db, &con) == DuckDBError) {
        fprintf(stderr, "Failed to create connection\n");
        duckdb_close(&db);
        return 1;
    }

    // Create table
    if (duckdb_query(con, "CREATE TABLE integers(i INTEGER, j INTEGER)", NULL) == DuckDBError) {
        fprintf(stderr, "Failed to create table\n");
        goto cleanup;
    }

    // Insert data
    if (duckdb_query(con, "INSERT INTO integers VALUES (3, 4), (5, 6)", NULL) == DuckDBError) {
        fprintf(stderr, "Failed to insert data\n");
        goto cleanup;
    }

    // Query data
    if (duckdb_query(con, "SELECT * FROM integers", &result) == DuckDBError) {
        fprintf(stderr, "Failed to query\n");
        goto cleanup;
    }

    // Process results
    idx_t row_count = duckdb_row_count(&result);
    idx_t col_count = duckdb_column_count(&result);

    for (idx_t i = 0; i < col_count; i++) {
        printf("%s ", duckdb_column_name(&result, i));
    }
    printf("\n");

    for (idx_t row = 0; row < row_count; row++) {
        for (idx_t col = 0; col < col_count; col++) {
            char *val = duckdb_value_varchar(&result, col, row);
            printf("%s ", val);
            duckdb_free(val);
        }
        printf("\n");
    }

    duckdb_destroy_result(&result);

cleanup:
    duckdb_disconnect(&con);
    duckdb_close(&db);
    return 0;
}

Memory Management

The C API requires manual memory management. Always:
  • Check return codes
  • Free allocated resources
  • Call duckdb_free() on strings returned by the API
  • Destroy results with duckdb_destroy_result()

Thread Safety

  • A duckdb_database can be shared across threads
  • Each thread should have its own duckdb_connection
  • Do not share connections between threads

Next Steps

Connection Management

Learn about database and connection lifecycle

Query Execution

Execute queries and handle results

Prepared Statements

Use parameterized queries

Data Types

Work with DuckDB data types