← All posts
7 min read

What an API Provides: A Supported Contract at a Software Boundary

#technology#api#application-architecture#software-design
📑 On this page

An API is often described as a way for one program to talk to another. That is true, but incomplete.

An application programming interface defines the supported operations, data, rules, and expectations at a software boundary.

The URL or function name is only the entry point. The contract includes what the operation means, who may call it, how it fails, and how it can change.

A concrete example: account details

A mobile app requests:

GET /accounts/1042

The API might return:

{
  "id": "1042",
  "displayName": "Mira Rao",
  "membership": "gold"
}

The app does not need to know:

  • Which database stores the account
  • Whether data came from a cache
  • Which internal services were called
  • How records are joined

Those details can change while the supported response remains compatible.

APIs exist inside and across systems

An API can be:

  • A function or class interface
  • An operating-system API
  • A library API
  • A database interface
  • A network service
  • A hardware interface

This article focuses mainly on service APIs, but the same design principle applies: callers depend on a published boundary rather than implementation internals.

An API is not necessarily public. Internal interfaces still create dependencies and deserve clear contracts.

Capabilities, not database exposure

A useful API exposes meaningful operations:

  • Create order
  • Cancel subscription
  • List unpaid invoices
  • Approve refund

Direct database access exposes:

  • Tables
  • Storage schema
  • Migration timing
  • Internal identifiers
  • Broad write power

An API can enforce authorization, invariants, audit, and workflow around data.

Returning table rows may be convenient, but the public contract should express domain meaning rather than mirror storage accidentally.

Requests define intent

A request includes:

  • Operation
  • Resource
  • Input data
  • Caller identity
  • Context

The API should make intent unambiguous.

Compare:

POST /doAction { "code": 17, "flag": true }

with:

POST /orders/9001/cancellation
{ "reason": "customer_request" }

The second is easier to document, authorize, audit, and evolve.

Generic endpoints often move complexity into undocumented magic values.

Responses communicate results

A response can include:

  • Created or requested data
  • Status
  • Identifiers
  • Pagination
  • Warnings
  • Links to related operations
  • Error details

Return enough information for the caller to continue safely.

After creating an order, returning its authoritative identifier and state can prevent the client from guessing what happened.

Do not expose sensitive internal fields merely because they are available.

Response shape is part of the compatibility promise.

Semantics matter more than syntax

Two APIs can use identical JSON but mean different things.

What does status: "complete" mean?

  • Payment captured?
  • Shipment delivered?
  • Background processing finished?

Contracts need semantic definitions:

  • Field meaning
  • Units
  • Time zone
  • Null behavior
  • State transitions
  • Ordering
  • Consistency

Schema validation proves shape, not meaning.

Many integration failures come from semantic assumptions that were never written down.

Error behavior is part of the API

Callers need to know whether to:

  • Correct input
  • Ask the user
  • Authenticate again
  • Retry later
  • Stop permanently
  • Contact support

Stable error codes and structured details support decisions.

Do not leak stack traces, SQL, secrets, or internal network names.

Errors should be useful to authorized callers and safe for attackers to see.

Document retryable and nonretryable outcomes.

Identity and authorization

An API must determine:

  • Who or what is calling?
  • Which action may that identity perform?
  • On which resource?
  • Under which tenant or context?

Authentication may use sessions, tokens, certificates, or signed requests.

Authorization must occur at the server boundary even if the client hides unavailable actions.

Service identities should receive least privilege, and delegated access should preserve the end user's authority where required.

Operational rules belong in the contract

Callers also depend on:

  • Rate limits
  • Maximum request size
  • Timeout expectations
  • Pagination
  • Idempotency
  • Event-delivery guarantees
  • Retention
  • Availability targets

An endpoint that accepts an upload but silently rejects files over an undocumented size is incomplete.

Operational behavior affects architecture and user experience as much as field names.

Publish limits and provide machine-readable signals where possible.

Idempotency protects repeated requests

Networks can time out after the server completes an operation.

The client cannot know whether to retry.

For operations such as payment or order creation, an idempotency key lets the server recognize a repeat:

Idempotency-Key: checkout-1042-attempt-7

The server returns the original result instead of applying the action twice.

The contract should define key scope, lifetime, and behavior when the same key is reused with different input.

APIs support independent evolution

A database can move from one technology to another if callers depend only on the API contract.

The provider can:

  • Add caching
  • Split services
  • Change queries
  • Migrate data

without forcing every client to change.

This independence has limits.

Changes in latency, consistency, ordering, or error behavior can break callers even when JSON remains identical.

Compatibility includes observed semantics.

Documentation is part of the product

Useful API documentation includes:

  • Purpose
  • Authentication
  • Operations
  • Examples
  • Schemas
  • Errors
  • Limits
  • Pagination
  • Versioning
  • Changelog
  • Support policy

Examples help readers begin, while formal schemas support tooling.

Documentation should be tested against real behavior.

Generated reference docs are valuable but do not replace explanations of workflows, state transitions, and common failure handling.

Specifications and generated tooling

Formats such as OpenAPI or GraphQL schemas can describe interfaces in a machine-readable form.

Tools can generate:

  • Client libraries
  • Validation
  • Interactive documentation
  • Mocks
  • Contract tests

Generation reduces repetitive work but can produce awkward clients if the underlying API is poorly designed.

The specification should remain authoritative and reviewed with implementation changes.

Do not let generated code hide breaking semantic changes.

SDKs improve language ergonomics

An SDK wraps an API in language-friendly operations:

const order = await orders.create(input);

It may handle:

  • Authentication headers
  • Serialization
  • Retries
  • Pagination
  • Error mapping
  • Telemetry

The SDK becomes another compatibility surface.

Avoid hidden retries for nonidempotent operations and expose enough details for diagnosis.

Users may call the raw API from unsupported languages, so the network contract remains fundamental.

Observability and correlation

APIs should provide and preserve request identifiers.

Operators need to trace:

client request -> gateway -> service -> database -> dependency

Monitor:

  • Traffic
  • Latency
  • Errors
  • Saturation
  • Authentication failures
  • Rate-limit responses
  • Usage by operation

Logs must avoid sensitive request bodies and credentials.

Operational visibility is part of supporting the contract.

Governance without paralysis

API governance can standardize:

  • Naming
  • Error format
  • Authentication
  • Versioning
  • Review
  • Documentation

Consistency reduces caller learning.

Overly rigid centralized approval can delay useful work and encourage bypasses.

Provide paved paths, reusable tooling, and automated checks.

Allow justified exceptions with recorded reasons.

Knowledge check

  1. Why is an API more than a URL?
  2. What is lost when an API exposes raw database tables?
  3. Why can a schema-valid response still be semantically ambiguous?
  4. What problem does an idempotency key solve?
  5. Which behavior can break clients even when response fields do not change?

The one idea to remember

An API is a supported behavioral contract. It exposes meaningful capability while defining data, identity, errors, limits, and compatibility so callers and implementations can evolve without sharing internals.