# Understanding the OpenMetadata API for Developers: A Complete Technical Guide

> Explore the OpenMetadata API: a powerful RESTful service with a type-safe Java SDK. Master CRUD operations, versioning, and pagination for seamless data discovery and governance.

- Repository: [OpenMetadata/OpenMetadata](https://github.com/open-metadata/OpenMetadata)
- Tags: api-reference
- Published: 2026-04-23

---

**The OpenMetadata API is a RESTful service built on Java 21 and Dropwizard, with a type-safe Java SDK that abstracts HTTP calls behind service classes for CRUD operations, versioning, and pagination.**

This guide examines the **OpenMetadata API for developers** based on the actual source code implementation. Whether you're building integrations, extending the platform, or debugging client-server interactions, understanding the architectural layers—from the low-level HTTP client to the generated entity models—enables you to work effectively with this metadata management platform.

## Architecture Overview: How the OpenMetadata API Layers Work

The OpenMetadata API architecture separates concerns across four distinct layers, each with clear responsibilities and well-defined interfaces.

### Backend Services Layer (Dropwizard Resources)

All metadata entities—tables, services, pipelines, dashboards, and more—are modeled as **resources** that expose RESTful endpoints. These resources live in `openmetadata-service/src/main/java/org/openmetadata/service/resources/**` and follow JAX-RS conventions with Swagger annotations.

Each resource class defines:
- **Path patterns** (e.g., `/tables`, `/apiServices/{id}`)
- **HTTP methods** (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`)
- **Query parameters** for filtering, field selection, and pagination
- **Request/response models** using generated POJOs

For example, [`APIServiceResource.java`](https://github.com/open-metadata/OpenMetadata/blob/main/APIServiceResource.java) defines the contract for API service management, while `TableResource` handles data asset operations.

### HTTP Transport Layer (OkHttp Wrapper)

The SDK uses a thin wrapper around OkHttp called `OpenMetadataHttpClient`. This class, located at [`openmetadata-sdk/src/main/java/org/openmetadata/sdk/network/OpenMetadataHttpClient.java`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-sdk/src/main/java/org/openmetadata/sdk/network/OpenMetadataHttpClient.java), handles:

- **Request construction**: Building `Request` objects with proper headers and body serialization
- **Authentication header injection**: Adding `Authorization: Bearer <token>` or `X-Auth-Params-Email` for test mode
- **JSON parsing**: Deserializing responses using Jackson into generated model classes
- **Error conversion**: Mapping HTTP status codes to typed exceptions

The `execute()` method is the core entry point, accepting an HTTP method, path, optional body, and response type parameter.

### SDK Client Layer (Type-Safe Service Classes)

The `OpenMetadataClient` class serves as the public entry point for SDK users. Located at [`openmetadata-sdk/src/main/java/org/openmetadata/sdk/client/OpenMetadataClient.java`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-sdk/src/main/java/org/openmetadata/sdk/client/OpenMetadataClient.java), it exposes getter methods for every service:

- `tables()` → `TableService`
- `apiServices()` → `ApiServiceService`
- `pipelines()` → `PipelineService`
- `glossaries()` → `GlossaryService`

Each service class (e.g., `TableService` at [`openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/dataassets/TableService.java`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/dataassets/TableService.java)) wraps HTTP calls behind intuitive CRUD methods like `list()`, `getById()`, `create()`, `update()`, and `delete()`.

### Generated Entity Models

All API payloads are represented by **generated POJOs** located in `openmetadata-sdk/src/main/java/org/openmetadata/sdk/models/**`. These classes mirror the JSON schema definitions from `openmetadata-spec/` and include:

- Core entities: `Table`, `ApiService`, `Pipeline`, `Dashboard`, `Topic`
- Request objects: `CreateTable`, `CreateApiService`
- Response wrappers: `ResultList<T>`, `EntityHistory`
- Supporting types: `EntityReference`, `TagLabel`, `Column`

Jackson annotations enable seamless serialization and deserialization throughout the stack.

## Authentication and Security Configuration

The **OpenMetadata API for developers** supports two authentication modes, configured through `OpenMetadataConfig` at [`openmetadata-sdk/src/main/java/org/openmetadata/sdk/config/OpenMetadataConfig.java`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-sdk/src/main/java/org/openmetadata/sdk/config/OpenMetadataConfig.java).

### JWT Authentication (Production)

```java
OpenMetadataConfig config = OpenMetadataConfig.builder()
    .serverUrl("https://openmetadata.mycompany.com/api")
    .accessToken("eyJhbGciOiJIUzI1NiIs...")
    .connectionTimeout(Duration.ofSeconds(30))
    .readTimeout(Duration.ofSeconds(60))
    .build();

OpenMetadataClient client = new OpenMetadataClient(config);

```

The SDK automatically adds `Authorization: Bearer <accessToken>` to every request.

### Test Mode (Email Token)

For development or testing environments:

```java
OpenMetadataConfig config = OpenMetadataConfig.builder()
    .serverUrl("http://localhost:8585/api")
    .accessToken("admin@open-metadata.org")  // email as token
    .build();

```

The `OpenMetadataHttpClient.buildRequest` method detects this format and sends `X-Auth-Params-Email: admin@open-metadata.org` instead.

## Core API Patterns: CRUD, Pagination, and Versioning

Understanding these three patterns is essential for productive **OpenMetadata API** development.

### CRUD Operations

Every entity service follows a consistent CRUD pattern. Here's the complete lifecycle for a table:

```java
// CREATE
CreateTable createRequest = new CreateTable()
    .withName("customer_orders")
    .withDatabaseSchema(new EntityReference()
        .withId(schemaId)
        .withType("databaseSchema"))
    .withColumns(List.of(
        new Column().withName("order_id").withDataType(ColumnDataType.INT),
        new Column().withName("customer_name").withDataType(ColumnDataType.VARCHAR)
            .withDataLength(255)));

Table createdTable = client.tables().create(createRequest);
UUID tableId = createdTable.getId();

// READ
Table retrievedTable = client.tables().getById(tableId, "columns,owner,tags");

// UPDATE (partial)
Table updatedTable = client.tables().patch(tableId, patchOperations);

// DELETE
client.tables().delete(tableId, true);  // hard delete

```

The `TableService` class implements these methods by delegating to `OpenMetadataHttpClient.execute()` with appropriate HTTP verbs: `POST` for create, `GET` for read, `PUT`/`PATCH` for update, `DELETE` for removal.

### Pagination with ResultList

All list endpoints return `ResultList<T>`, defined at [`openmetadata-sdk/src/main/java/org/openmetadata/sdk/models/ResultList.java`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-sdk/src/main/java/org/openmetadata/sdk/models/ResultList.java). This wrapper provides:

```java
public class ResultList<T> {
    private List<T> data;           // actual entities
    private Paging paging;        // cursor-based pagination info
    private int total;            // total matching records
}

```

Practical pagination example:

```java
String afterCursor = null;
do {
    ResultList<Table> result = client.tables().list(
        null,           // uriInfo
        null,           // securityContext
        "columns,owner", // fields
        null,           // filter
        50,             // limit
        null,           // before cursor
        afterCursor,    // after cursor
        Include.NON_DELETED);

    result.getData().forEach(table -> 
        System.out.println(table.getFullyQualifiedName()));

    afterCursor = result.getPaging() != null 
        ? result.getPaging().getAfter() 
        : null;
} while (afterCursor != null);

```

The `before` and `after` parameters implement **cursor-based pagination**, more efficient than offset-based approaches for large datasets.

### Entity Versioning

Every metadata entity in OpenMetadata maintains full version history. The `VersionResource` at [`openmetadata-service/src/main/java/org/openmetadata/service/resources/version/VersionResource.java`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-service/src/main/java/org/openmetadata/service/resources/version/VersionResource.java) powers this capability.

Retrieving version history:

```java
// Get all versions
EntityHistory history = client.apiServices().listVersions(serviceId, 20, 0, null);

// Get specific version
ApiService versionedService = client.apiServices()
    .getVersion(serviceId, "0.3");  // semantic version string

// Compare versions
EntityVersionComparison diff = client.apiServices()
    .compareVersions(serviceId, "0.2", "0.3");

```

The version system uses **semantic versioning** (major.minor) and tracks all changes including description updates, tag additions, schema modifications, and ownership changes.

## Error Handling and Exception Types

Robust **OpenMetadata API** integrations require proper error handling. The `OpenMetadataHttpClient.handleErrorResponse` method converts HTTP status codes to typed exceptions.

### Exception Hierarchy

| Exception Class | HTTP Status | Typical Cause |
|-----------------|-------------|---------------|
| `InvalidRequestException` | 400 | Malformed JSON, missing required fields, validation failures |
| `AuthenticationException` | 401 | Expired or invalid JWT, missing credentials |
| `AuthorizationException` | 403 | Insufficient permissions for the operation |
| `NotFoundException` | 404 | Entity doesn't exist or was hard-deleted |
| `ConflictException` | 409 | Unique constraint violation, concurrent modification |
| `RateLimitException` | 429 | Too many requests, includes `retryAfterSeconds` |
| `ApiException` | 500+ | Server errors, unexpected failures |

### Practical Error Handling

```java
import org.openmetadata.sdk.exceptions.*;

public Table safelyGetTable(OpenMetadataClient client, UUID tableId) {
    try {
        return client.tables().getById(tableId, "columns,owner");
    } catch (AuthenticationException e) {
        // Refresh token and retry
        String newToken = refreshAuthToken();
        client.getConfig().setAccessToken(newToken);
        return client.tables().getById(tableId, "columns,owner");
    } catch (NotFoundException e) {
        // Log and return null or default
        logger.warn("Table {} not found", tableId);
        return null;
    } catch (RateLimitException e) {
        // Exponential backoff
        try {
            Thread.sleep(e.getRetryAfterSeconds() * 1000L);
            return client.tables().getById(tableId, "columns,owner");
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(ie);
        }
    } catch (ApiException e) {
        // Unrecoverable server error
        throw new MetadataSystemException("Failed to retrieve table: " + e.getMessage(), e);
    }
}

```

## Raw HTTP API Reference (cURL Examples)

For developers preferring direct HTTP calls or building non-Java clients, here are the underlying REST endpoints that the SDK wraps.

### Authentication

```bash

# Obtain JWT token (if using email/password flow)

curl -X POST "https://openmetadata.mycompany.com/api/v1/login" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "admin@open-metadata.org",
    "password": "admin"
  }'

```

### List Tables

```bash
curl -X GET "https://openmetadata.mycompany.com/api/v1/tables?fields=columns,owner,tags&limit=25&include=non-deleted" \
  -H "Authorization: Bearer $OM_TOKEN" \
  -H "Accept: application/json"

```

Response structure matches `ResultList<Table>`:

```json
{
  "data": [
    {
      "id": "uuid-here",
      "name": "customer_orders",
      "fullyQualifiedName": "mydb.myschema.customer_orders",
      "columns": [...],
      "owner": {...}
    }
  ],
  "paging": {
    "after": "base64-cursor-string",
    "total": 150
  }
}

```

### Create API Service

```bash
curl -X POST "https://openmetadata.mycompany.com/api/v1/apiServices" \
  -H "Authorization: Bearer $OM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-rest-api",
    "serviceType": "API",
    "connection": {
      "endpoint": "https://api.mycompany.com",
      "authType": "Bearer",
      "bearerToken": "my-api-token"
    }
  }'

```

### Get Version History

```bash
curl -X GET "https://openmetadata.mycompany.com/api/v1/apiServices/{id}/versions?limit=10" \
  -H "Authorization: Bearer $OM_TOKEN"

```

## Key Source Files for Deep Dives

| File | Path | Purpose |
|------|------|---------|
| `OpenMetadataHttpClient` | [`openmetadata-sdk/src/main/java/org/openmetadata/sdk/network/OpenMetadataHttpClient.java`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-sdk/src/main/java/org/openmetadata/sdk/network/OpenMetadataHttpClient.java) | Low-level HTTP, header injection, error handling |
| `OpenMetadataClient` | [`openmetadata-sdk/src/main/java/org/openmetadata/sdk/client/OpenMetadataClient.java`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-sdk/src/main/java/org/openmetadata/sdk/client/OpenMetadataClient.java) | Public SDK entry point, service factory |
| `OpenMetadataConfig` | [`openmetadata-sdk/src/main/java/org/openmetadata/sdk/config/OpenMetadataConfig.java`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-sdk/src/main/java/org/openmetadata/sdk/config/OpenMetadataConfig.java) | Configuration: URL, token, timeouts |
| `TableService` | [`openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/dataassets/TableService.java`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/dataassets/TableService.java) | Example service implementation for tables |
| `ResultList` | [`openmetadata-sdk/src/main/java/org/openmetadata/sdk/models/ResultList.java`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-sdk/src/main/java/org/openmetadata/sdk/models/ResultList.java) | Pagination wrapper for all list endpoints |
| `APIServiceResource` | [`openmetadata-service/src/main/java/org/openmetadata/service/resources/services/apiservices/APIServiceResource.java`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/apiservices/APIServiceResource.java) | Backend REST endpoint definition |
| `VersionResource` | [`openmetadata-service/src/main/java/org/openmetadata/service/resources/version/VersionResource.java`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-service/src/main/java/org/openmetadata/service/resources/version/VersionResource.java) | Version history endpoints |
| `ErrorResponse` | [`openmetadata-sdk/src/main/java/org/openmetadata/sdk/models/ErrorResponse.java`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-sdk/src/main/java/org/openmetadata/sdk/models/ErrorResponse.java) | Error payload structure |

## Summary

- **The OpenMetadata API for developers** follows a layered architecture: Dropwizard backend resources, OkHttp transport, type-safe Java SDK, and generated entity models.

- **Authentication** supports JWT tokens (production) and email-based tokens (test mode), automatically injected by `OpenMetadataHttpClient`.

- **CRUD operations** are consistent across all entity types through dedicated service classes (`TableService`, `ApiServiceService`, etc.) that wrap HTTP calls.

- **Pagination** uses cursor-based `ResultList<T>` with `before`/`after` parameters for efficient large dataset handling.

- **Versioning** tracks complete entity history through `VersionResource` endpoints, accessible via `listVersions()` and `getVersion()` methods.

- **Error handling** converts HTTP status codes to typed exceptions (`InvalidRequestException`, `AuthenticationException`, `RateLimitException`, etc.) with server-provided messages.

## Frequently Asked Questions

### What programming languages does the OpenMetadata SDK support?

The official SDK is **Java-only** (requires Java 3.10+), located in the `openmetadata-sdk` module. For other languages, use the raw REST API directly—the backend exposes standard JSON endpoints that any HTTP client can consume. Community SDKs for Python and TypeScript exist but are not officially maintained.

### How do I handle rate limiting in the OpenMetadata API?

The SDK throws `RateLimitException` when receiving HTTP 429 responses. This exception includes `getRetryAfterSeconds()` for backoff timing. Implement exponential backoff with jitter for production reliability. At the HTTP level, watch for `Retry-After` headers in raw API responses.

### What's the difference between soft delete and hard delete in OpenMetadata?

The `include` parameter controls deletion visibility. `Include.NON_DELETED` (default) filters out soft-deleted entities. `Include.ALL` returns everything including soft-deleted items. `Include.DELETED` returns only soft-deleted entities. Hard deletion requires passing `hardDelete=true` to the delete method, which permanently removes the entity and all its version history—this action cannot be undone.