Understanding the OpenMetadata API for Developers: A Complete Technical Guide
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 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, handles:
- Request construction: Building
Requestobjects with proper headers and body serialization - Authentication header injection: Adding
Authorization: Bearer <token>orX-Auth-Params-Emailfor 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, it exposes getter methods for every service:
tables()→TableServiceapiServices()→ApiServiceServicepipelines()→PipelineServiceglossaries()→GlossaryService
Each service class (e.g., TableService at 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.
JWT Authentication (Production)
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:
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:
// 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. This wrapper provides:
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:
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 powers this capability.
Retrieving version history:
// 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
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
# 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
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>:
{
"data": [
{
"id": "uuid-here",
"name": "customer_orders",
"fullyQualifiedName": "mydb.myschema.customer_orders",
"columns": [...],
"owner": {...}
}
],
"paging": {
"after": "base64-cursor-string",
"total": 150
}
}
Create API Service
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
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
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>withbefore/afterparameters for efficient large dataset handling. -
Versioning tracks complete entity history through
VersionResourceendpoints, accessible vialistVersions()andgetVersion()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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →