# Create records

Learn how to create records in Aerospike using the Developer SDK. This guide covers inserting new records, upserting, setting TTL, and working with complex data types.

## Insert a single record

Use `insert()` to create a new record. This fails if the record already exists.

-   [Java](#tab-panel-3126)
-   [Python](#tab-panel-3127)

```java
DataSet users = DataSet.of("test", "users");

session.insert(users)

    .bins("name", "email", "age")

    .id("user-1").values("Alice Smith", "alice@example.com", 28)

    .execute();
```

```python
async def demo(session):

    users = DataSet.of("test", "users")

    await session.insert(key=users.id("user-1")).put(

        {

            "name": "Alice Smith",

            "email": "alice@example.com",

            "age": 28,

        }

    ).execute()
```

Pass a connected `Session` from your app entrypoint (see [Connect](https://aerospike.com/docs/develop/client/sdk/connect)).

## Upsert (insert or update)

Use `upsert()` when you want to create a record if it doesn’t exist, or update it if it does.

-   [Java](#tab-panel-3128)
-   [Python](#tab-panel-3129)

```java
// Creates or updates the record

session.upsert(users)

    .bins("name", "email", "age")

    .id("user-1").values("Alice Smith", "alice.smith@example.com", 29)

    .execute();
```

```python
async def demo(session):

    users = DataSet.of("test", "users")

    # Creates or updates the record

    await session.upsert(key=users.id("user-1")).put(

        {

            "name": "Alice Smith",

            "email": "alice.smith@example.com",

            "age": 29,

        }

    ).execute()
```

::: boolean bin values in verification tools
Aerospike stores booleans as integer-backed values. Some low-level tools and validation harnesses may display `true` as `1` and `false` as `0`. This is expected and does not mean the write failed.
:::

### Insert vs upsert vs other operations

| Method | Record Exists | Record Doesn’t Exist | Merges with Existing Data |
| --- | --- | --- | --- |
| `insert()` | ❌ Fails with error | ✅ Creates record | Not Applicable |
| `upsert()` | ✅ Updates record | ✅ Creates record | ✅ Yes |
| `update()` | ✅ Updates record | ❌ Fails with error | ✅ Yes |
| `replace()` | ✅ Updates record | ✅ Creates record | ❌ No |
| `replaceIfExists()` / `replace_if_exists()` | ✅ Updates record | ❌ Fails with error | ❌ No |

**Rule of thumb**: Use `insert()` when you expect the record to be new and want to catch duplicates. Use `upsert()` when you don’t care whether it exists.

## Set time-to-live (TTL)

Records can automatically expire after a specified duration, or at a specified time:

-   [Java](#tab-panel-3130)
-   [Python](#tab-panel-3131)

```java
import java.time.Duration;

import com.aerospike.client.sdk.AerospikeException;

// Record expires in 1 hour

try {

    session.insert(users)

        .bins("token", "user_id")

        .id("session-token").values("abc123", "user-1")

        .expireRecordAfter(Duration.ofHours(1))

        .execute();

} catch (AerospikeException e) {

    // Can fail with "Operation not allowed at this time" when TTL prerequisites are not met.

    System.err.println("TTL write failed: " + e.getMessage());

}

// Record expires on 1st January, 2030 (UTC)

session.insert(users)

    .bins("name")

    .id("user-1").values("Alice")

    .expireRecordAt(LocalDateTime.of(2030,1,1,0,0,0))

    .execute();

// Record never expires (override namespace default)

session.insert(users.id("permanent-user"))

    .bin("name").setTo("System Admin")

    .neverExpire()

    .execute();
```

```python
async def demo(session):

    users = DataSet.of("test", "users")

    # Record expires in 1 hour

    await session.insert(key=users.id("session-token")).put(

        {"token": "abc123", "user_id": "user-1"}

    ).expire_record_after_seconds(3600).execute()

    # Record expires in 30 days

    await session.insert(key=users.id("user-1")).put(

        {"name": "Alice"}

    ).expire_record_after_seconds(30 * 24 * 3600).execute()

    # Record never expires (override namespace default)

    await session.insert(key=users.id("permanent-user")).put(

        {"name": "System Admin"}

    ).never_expire().execute()
```

::: ttl behavior
If you don’t specify a TTL, the record inherits the namespace’s default TTL. In Java, call `neverExpire()`. In Python, call `never_expire()` (or use `expire_record_after_seconds(...)` for explicit TTL).
:::
::: ttl prerequisites on the server
Per-record TTL writes depend on namespace settings. If `nsup-period` is disabled (for example `0`) and `allow-ttl-without-nsup=false`, writes using TTL methods (`expireRecordAfter(...)` in Java, `expire_record_after_seconds(...)` in Python) can fail with `Operation not allowed at this time`. Enable NSUP or update namespace settings before relying on per-record TTL in examples/tests.
:::
::: create-only error handling in services
For create-only flows (`insert()`), handle key-already-exists outcomes (`RecordExistsException` in Java, or `AerospikeError` with `KEY_EXISTS_ERROR` in Python) and return a domain error instead of terminating the process. This keeps request handlers and test runners repeatable across reruns.
:::

## Store the user key

By default, Aerospike only stores a digest (hash) of the key. To retrieve the original key later, enable send-key on the behavior used by the session — `sendKey(true)` in Java, `send_key=True` in Python:

-   [Java](#tab-panel-3132)
-   [Python](#tab-panel-3133)

```java
import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

Behavior sendKeyBehavior = Behavior.DEFAULT.deriveWithChanges("sendKey", builder ->

    builder.on(Selectors.all(), op -> op.sendKey(true)));

Session session = cluster.createSession(sendKeyBehavior);

session.update(users)

    .bins("name")

    .id("user-12345").values("Alice")

    .execute();

// Later, when reading:

session.query(users.id("user-12345"))

    .execute()

    .getFirst()

    .ifPresent(result ->

        System.out.printf("key: %s\n", result.key().userKey).   // user-12345

    );

}
```

```python
async def demo(client):

    users = DataSet.of("test", "users")

    send_key_behavior = Behavior.DEFAULT.derive_with_changes(

        name="send_key", send_key=True,

    )

    session = client.create_session(send_key_behavior)

    await session.upsert(key=users.id("user-12345")).put({"name": "Alice"}).execute()

    stream = await session.query(users.id("user-12345")).execute()

    row = await stream.first_or_raise()

    record = row.record_or_raise()

    stream.close()

    print(record.key.value)  # "user-12345"
```

::: when to use send-key
Enable send-key on the behavior (`sendKey(true)` in Java, `send_key=True` in Python) when you need to:

-   Scan records and know their original keys
-   Debug or audit record creation
-   Store human-readable identifiers alongside records
:::

## Create with complex data types

Aerospike supports lists and maps as bin values:

-   [Java](#tab-panel-3134)
-   [Python](#tab-panel-3135)

```java
import java.util.List;

import java.util.Map;

// List values

session.insert(users)

    .bins("name", "tags", "scores")

    .id("user-1").values(

        "Alice",

        List.of("premium", "verified", "active"),

        List.of(95, 87, 92, 88)

    )

    .execute();

// Map values

session.insert(users.id("user-1")

    .bin("name").setTo("Alice")

    .bin("preferences").setTo(

        Map.of("theme", "dark", "language", "en", "notifications", true));

    .execute();

// Nested structures

session.insert(users)

    .bins("name", "address")

    .id("user-1").values(

        "Alice",

        Map.of(

            "street", "123 Main St",

            "city", "San Francisco",

            "coordinates", List.of(37.7749, -122.4194)

        )

    )

    .execute();
```

```python
async def demo(session):

    users = DataSet.of("test", "users")

    # List values

    await session.insert(key=users.id("user-1")).put(

        {

            "name": "Alice",

            "tags": ["premium", "verified", "active"],

            "scores": [95, 87, 92, 88],

        }

    ).execute()

    # Map/dict values

    await session.insert(key=users.id("user-2")).put(

        {

            "name": "Alice",

            "preferences": {

                "theme": "dark",

                "language": "en",

                "notifications": True,

            },

        }

    ).execute()

    # Nested structures

    await session.insert(key=users.id("user-3")).put(

        {

            "name": "Alice",

            "address": {

                "street": "123 Main St",

                "city": "San Francisco",

                "coordinates": [37.7749, -122.4194],

            },

        }

    ).execute()
```

> 📖 **Learn more**: [Data Model](https://aerospike.com/docs/develop/client/sdk/concepts/data-model) explains Aerospike’s data types in detail.

## Create only if not exists

Use `insert()` with error handling to implement “create only if not exists” logic:

-   [Java](#tab-panel-3136)
-   [Python](#tab-panel-3137)

```java
import com.aerospike.client.sdk.AerospikeException;

try {

    session.insert(users)

        .bins("name")

        .id("user-1").values("Alice")

        .execute();

    System.out.println("User created successfully");

} catch (AerospikeException.RecordExistsException e) {

    System.out.println("User already exists, skipping creation");

}
```

```python
from aerospike_sdk import AerospikeError

from aerospike_async.exceptions import ResultCode

async def demo(session):

    users = DataSet.of("test", "users")

    try:

        await session.insert(key=users.id("user-1")).put({"name": "Alice"}).execute()

        print("User created successfully")

    except AerospikeError as e:

        if e.result_code != ResultCode.KEY_EXISTS_ERROR:

            raise

        print("User already exists, skipping creation")
```

> 📖 **Learn more**: [Error Handling](https://aerospike.com/docs/develop/client/sdk/concepts/errors) covers all exception types.

## Batch create

For creating multiple records efficiently, see [Batch Operations](https://aerospike.com/docs/develop/client/sdk/usage/batch).

-   [Java](#tab-panel-3138)
-   [Python](#tab-panel-3139)

```java
// Quick preview - see Batch Operations for full details

session.insert(users)

    .bins("name")

    .id("user-1").values("Alice")

    .id("user-2").values("Bob")

    .id("user-3").values("Carol")

    .execute();
```

```python
async def demo(session):

    users = DataSet.of("test", "users")

    await (

        session.batch()

        .insert(users.id("user-1")).put({"name": "Alice"})

        .insert(users.id("user-2")).put({"name": "Bob"})

        .insert(users.id("user-3")).put({"name": "Carol"})

        .execute()

    )
```

## Complete example

-   [Java](#tab-panel-3140)
-   [Python](#tab-panel-3141)

```java
import com.aerospike.client.sdk.Cluster;

import com.aerospike.client.sdk.ClusterDefinition;

import com.aerospike.client.sdk.DataSet;

import com.aerospike.client.sdk.Session;

import com.aerospike.client.sdk.policy.Behavior;

import java.util.List;

import java.util.Map;

public class CreateRecordsExample {

    public static void main(String[] args) {

        try (Cluster cluster = new ClusterDefinition("localhost", 3000).connect()) {

            Session session = cluster.createSession(Behavior.DEFAULT);

            DataSet users = DataSet.of("test", "users");

            String key = "create-example-user";

            // Cleanup so the example is repeatable.

            session.delete(users.id(key)).execute();

            // Simple insert

            session.insert(users)

                .bins("name", "email", "age")

                .id(key).values("Alice Smith", "alice@example.com", 28)

                .execute();

            // Upsert with complex data

            session.upsert(users)

                .bins("name", "tags", "preferences")

                .id(key).values(

                    "Alice Smith",

                    List.of("premium", "verified"),

                    Map.of("theme", "dark")

                )

                .execute();

            System.out.println("Records created successfully!");

        }

    }

}
```

```python
import asyncio

from aerospike_sdk import Behavior, DataSet, Client

async def main():

    async with Client("localhost:3000") as client:

        session = client.create_session(Behavior.DEFAULT)

        users = DataSet.of("test", "users")

        key = users.id("create-example-user")

        # Cleanup so the example is repeatable.

        stream = await session.delete(key=key).execute()

        stream.close()

        # Simple insert

        await session.insert(key=key).put(

            {

                "name": "Alice Smith",

                "email": "alice@example.com",

                "age": 28,

            }

        ).execute()

        # Upsert with complex data

        await session.upsert(key=key).put(

            {

                "name": "Alice Smith",

                "tags": ["premium", "verified"],

                "preferences": {"theme": "dark"},

            }

        ).execute()

        print("Records created successfully!")

if __name__ == "__main__":

    asyncio.run(main())
```

## API reference summary

| Method | Description | Link |
| --- | --- | --- |
| `insert()` | Create a new record (fails if exists) | Java · Python |
| `upsert()` | Create or update a record | Java · Python |
| `.bins(...)` / `.put({...})` | Set bin values | — |
| `.expireRecordAfter(duration)` / `.expire_record_after_seconds(seconds)` | Set time-to-live | — |
| `.expireRecordsAt(localDateTime)` (Java only) | Expire records at a wall-clock time | — |
| `.neverExpire()` / `.never_expire()` | Pin record TTL to never expire | — |

## Next steps

Read Records

Retrieve the records you’ve created.

[Read Records →](https://aerospike.com/docs/develop/client/sdk/usage/read)

Update Records

Modify existing records and bins.

[Update Records →](https://aerospike.com/docs/develop/client/sdk/usage/update)

Batch Operations

Create multiple records efficiently.

[Batch Operations →](https://aerospike.com/docs/develop/client/sdk/usage/batch)

Data Model

Understand namespaces, sets, and bins.

[Data Model →](https://aerospike.com/docs/develop/client/sdk/concepts/data-model)