# Read records

Learn how to read records from Aerospike using the Developer SDK. This guide covers getting records by key, selecting specific bins, handling missing records, and reading metadata.

## Get a record by key

Use `query()` with a key to read a single record (one row in the stream):

-   [Java](#tab-panel-3180)
-   [Python](#tab-panel-3181)

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

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

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

RecordStream stream = session.query(users.id("user-1")).execute();

stream.getFirst().ifPresent(result -> {

    if (result.isOk()) {

        Record user = result.recordOrThrow();

        System.out.println("Name: " + user.getString("name"));

        System.out.println("Email: " + user.getString("email"));

        System.out.println("Age: " + user.getInt("age"));

    }

});

stream.close();
```

```python
async def demo(session):

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

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

    row = await stream.first_or_raise()

    user = row.record_or_raise()

    stream.close()

    print(f"Name: {user.bins.get('name')}")

    print(f"Email: {user.bins.get('email')}")

    print(f"Age: {user.bins.get('age')}")
```

## Handle missing records

When a record doesn’t exist, the stream has no row (or the row signals not found):

-   [Java](#tab-panel-3182)
-   [Python](#tab-panel-3183)

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

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

RecordStream stream = session.query(users.id("nonexistent")).execute();

if (stream.getFirst().isEmpty()) {

    System.out.println("Record not found");

}

stream.close();

// Or use a default when present

RecordStream s2 = session.query(users.id("user-1")).execute();

String user = s2.getFirst()

    .filter(RecordResult::isOk)

    .map(result-> result.recordOrThrow().getString("name"))

    .orElse("Not found");

s2.close();

// Or throw if not found

Record s3 = session.query(users.id("user-1"))

    .includeMissingKeys()    // Include errors for missing records in stream

    .execute()

    .getFirstRecord();    // Throws if record is missing
```

```python
async def demo(session):

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

    stream = await session.query(users.id("nonexistent")).execute()

    row = await stream.first()

    if row is None:

        print("Record not found")

    stream.close()

    # Or use a default when present

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

    row = await stream.first()

    if row is None:

        name = "Unknown"

    else:

        user = row.record_or_raise()

        name = user.bins.get("name")

    stream.close()
```

## Select specific bins

By default, a key query returns all bins. Use `bins()` to retrieve only the bins you need:

-   [Java](#tab-panel-3184)
-   [Python](#tab-panel-3185)

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

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

// Only fetch name and email

RecordStream stream = session.query(users.id("user-1"))

    .readingOnlyBins("name", "email")

    .execute();

stream.getFirst().ifPresent(result -> {

    if (result.isOk()) {

        Record user = result.recordOrThrow();

        System.out.println("Name: " + user.getString("name"));

        System.out.println("Email: " + user.getString("email"));

        // user.getInt("age") would be null - not fetched

    }

});

stream.close();
```

```python
async def demo(session):

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

    # Only fetch name and email

    stream = await (

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

        .bins(["name", "email"])

        .execute()

    )

    row = await stream.first_or_raise()

    user = row.record_or_raise()

    stream.close()

    print(f"Name: {user.bins.get('name')}")

    print(f"Email: {user.bins.get('email')}")

    # user.bins.get("age") would be None - not fetched
```

::: performance
Selecting only needed bins reduces network transfer and improves performance, especially for records with large or many bins.
:::

## Read record metadata

Records have metadata you can access:

-   [Java](#tab-panel-3186)
-   [Python](#tab-panel-3187)

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

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

RecordStream stream = session.query(users.id("user-1")).execute();

stream.getFirst().ifPresent(result -> {

    if (result.isOk()) {

        Record user = result.recordOrThrow();

        // Generation: incremented on each update

        int generation = user.generation;

        // TTL: seconds until expiration (0 = never)

        int ttlSeconds = user.getTimeToLive();

        // User key (if sendKey was true during write)

        String key = result.key().userKey;

        System.out.println("Generation: " + generation);

        System.out.println("TTL: " + ttlSeconds + "s");

        System.out.println("Key: " + key);

    }

});

stream.close();
```

```python
async def demo(session):

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

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

    row = await stream.first_or_raise()

    user = row.record_or_raise()

    stream.close()

    generation = user.generation          # incremented on each update

    ttl_seconds = user.ttl                # seconds until expiration (0 = never)

    key = row.key.value                   # user key (if send_key was True during write)

    print(f"Generation: {generation}")

    print(f"TTL: {ttl_seconds}s")

    print(f"Key: {key}")
```

| Metadata | Description |
| --- | --- |
| **Generation** | Version counter, incremented on each update. Use for optimistic locking. |
| **TTL** | Seconds until expiration. 0 means never expires. -1 means use namespace default. |
| **Key** | Original user key (only available if send-key was enabled on the writing behavior — `sendKey(true)` in Java, `send_key=True` in Python). |

## Read with type safety

Use typed getters for bin values:

-   [Java](#tab-panel-3188)
-   [Python](#tab-panel-3189)

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

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

import java.util.List;

import java.util.Map;

RecordStream stream = session.query(users.id("user-1")).execute();

Record user = stream.getFirst()

    .filter(RecordResult::isOk)

    .map(RecordResult::recordOrThrow)

    .orElseThrow();

stream.close();

// String values

String name = user.getString("name");

// Integer values

int age = user.getInt("age");

long bigNumber = user.getLong("big_number");

// Double values

double balance = user.getDouble("balance");

// Boolean values

boolean active = user.getBoolean("active");

// List values

List<String> tags = user.getList("tags");

// Map values

Map<String, Object> preferences = user.getMap("preferences");

// Raw value (when type is unknown)

Object value = user.getValue("unknown_field");
```

```python
async def demo(session):

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

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

    row = await stream.first_or_raise()

    user = row.record_or_raise()

    stream.close()

    name: str = user.bins["name"]

    age: int = user.bins["age"]

    balance: float = user.bins["balance"]

    active: bool = user.bins["active"]

    tags: list = user.bins["tags"]

    preferences: dict = user.bins["preferences"]

    value = user.bins.get("unknown_field")  # None if absent
```

## Check if a record exists

To check existence without fetching data:

-   [Java](#tab-panel-3190)
-   [Python](#tab-panel-3191)

```java
boolean exists = session.exists(users.id("user-1")).execute().getFirstBoolean().orElse(false);

if (exists) {

    System.out.println("User exists");

} else {

    System.out.println("User not found");

}
```

```python
async def demo(session):

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

    stream = await session.exists(users.id("user-1")).execute()

    row = await stream.first()

    stream.close()

    if row is not None and row.as_bool():

        print("User exists")

    else:

        print("User not found")
```

::: use exists() for existence checks
`exists()` is faster than `query()` when you only need to check if a record exists—it doesn’t transfer bin data.
:::

## Read header only

To get only metadata (generation, TTL) without bin values:

-   [Java](#tab-panel-3192)
-   [Python](#tab-panel-3193)

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

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

RecordStream stream = session.query(users.id("user-1"))

    .withNoBins()

    .execute();

stream.getFirst().ifPresent(result -> {

    if (result.isOk()) {

        Record header = result.recordOrThrow();

        System.out.println("Generation: " + header.generation);

        System.out.println("TTL: " + header.getTimeToLive());

        // Bins are not populated

    }

});

stream.close();
```

```python
async def demo(session):

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

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

    row = await stream.first_or_raise()

    header = row.record_or_raise()

    stream.close()

    print(f"Generation: {header.generation}")

    print(f"TTL: {header.ttl}")

    # header.bins is {} — no bin payload fetched
```

## Batch read

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

-   [Java](#tab-panel-3194)
-   [Python](#tab-panel-3195)

```java
import java.util.List;

// Quick preview - see Batch Operations for full details

RecordStream usersStream = session.query(users.ids("user-1", "user-2", "user-3")).execute();

List<Record> users = usersStream.stream().toList();
```

```python
async def demo(session):

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

    stream = await session.query(users.ids("user-1", "user-2", "user-3")).execute()

    results = await stream.collect()

    records = [r.record_or_raise() for r in results if r.is_ok]
```

## Complete example

-   [Java](#tab-panel-3196)
-   [Python](#tab-panel-3197)

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

import com.aerospike.client.sdk.ClusterDefinition;

import com.aerospike.client.sdk.DataSet;

import com.aerospike.client.sdk.Record;

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

import com.aerospike.client.sdk.Session;

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

public class ReadRecordsExample {

    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 = "read-example-user";

            // Seed data so the example is repeatable.

            session.upsert(users)

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

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

                .execute();

            // Full record read

            RecordStream full = session.query(users.id(key)).execute();

            full.getFirst().ifPresent(result -> {

                if (result.isOk()) {

                    Record user = result.recordOrThrow();

                    System.out.println("=== Full Record ===");

                    System.out.println("Name: " + user.bins.get("name"));

                    System.out.println("Generation: " + user.generation);

                }

            });

            full.close();

            // Partial read

            RecordStream partial = session.query(users.id(key))

                .readingOnlyBins("name", "email")

                .execute();

            partial.getFirst().ifPresent(result -> {

                if (result.isOk()) {

                    Record user = result.recordOrThrow();

                    System.out.println("\n=== Selected Bins ===");

                    System.out.println("Name: " + user.getString("name"));

                    System.out.println("Email: " + user.getString("email"));

                }

            });

            partial.close();

            // Existence check

            boolean exists = session.exists(users.id(key)).execute().getFirstBoolean().orElse(false);

            System.out.println("\nUser exists: " + exists);

        }

    }

}
```

```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("read-example-user")

        # Seed data so the example is repeatable.

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

            {"name": "Alice Smith", "email": "alice@example.com", "age": 28}

        ).execute()

        # Full record read

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

        row = await stream.first_or_raise()

        user = row.record_or_raise()

        stream.close()

        print("=== Full Record ===")

        print(f"Name: {user.bins.get('name')}")

        print(f"Generation: {user.generation}")

        # Partial read

        stream = await (

            session.query(key)

            .bins(["name", "email"])

            .execute()

        )

        row = await stream.first_or_raise()

        user = row.record_or_raise()

        stream.close()

        print("\n=== Selected Bins ===")

        print(f"Name: {user.bins.get('name')}")

        print(f"Email: {user.bins.get('email')}")

        # Existence check

        # exists() yields a row only when the record is found; for a missing

        # key the stream is empty, so use first() + None check rather than

        # first_or_raise().

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

        row = await stream.first()

        exists = row is not None and row.as_bool()

        stream.close()

        print(f"\nUser exists: {exists}")

if __name__ == "__main__":

    asyncio.run(main())
```

## API reference summary

| Method | Description | Link |
| --- | --- | --- |
| `query(key)` | Read a record by key (single-row stream) | Java · Python |
| `exists()` | Check if a record exists | Java · Python |
| `.readingOnlyBins(...)` / `.bins([...])` | Bin projection on queries (`session.query(DataSet)`); see [Query records](https://aerospike.com/docs/develop/client/sdk/usage/query) | — |
| `.withNoBins()` / `.with_no_bins()` | Read only metadata (no bin payload) | — |

## Next steps

Update Records

Modify the records you’ve read.

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

Query Records

Find records with DSL queries.

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

Batch Operations

Read multiple records efficiently.

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