← Back to DevBytes

Generating OpenAPI Specs from Natural Language

Generating OpenAPI Specs from Natural Language

Writing OpenAPI specifications by hand is tedious, error-prone, and often the bottleneck in API-first development workflows. Developers describe endpoints in plain English during planning meetings, then spend hours translating those descriptions into YAML. Generating OpenAPI specs from natural language bridges that gap, letting teams move from idea to contract in seconds.

What Is Natural Language to OpenAPI Generation?

Natural language to OpenAPI generation is the process of using large language models (LLMs) or specialized parsers to convert human-readable API descriptions into valid OpenAPI 3.x specifications. You provide a prompt like "Create an API for managing a bookstore with books, authors, and reviews," and the system outputs a structured spec with paths, components, schemas, and responses.

This approach typically relies on one of two strategies:

Why It Matters

API design is fundamentally a communication problem. Stakeholders talk in natural language, but the deliverable is a machine-readable contract. Manual translation introduces several pain points:

The generated spec becomes a starting point, not a final artifact. Human review remains essential, but the heavy lifting of scaffolding is automated.

How to Generate OpenAPI Specs from Natural Language

Approach 1: Direct LLM Prompting

The simplest method is to call an LLM API with a carefully crafted system prompt. The key is constraining the model to output valid YAML and to include all required OpenAPI fields.

import openai
import yaml

SYSTEM_PROMPT = """You are an API architect. Given a natural language
description, produce a valid OpenAPI 3.1 specification in YAML.
Rules:
- Include openapi, info, paths, and components sections.
- Define reusable schemas in components/schemas.
- Use proper HTTP methods and status codes.
- Include request bodies and response examples.
- Output ONLY valid YAML, no markdown fences.
"""

user_description = """
Build an API for a task management application. Users can create projects,
add tasks to projects, mark tasks complete, and list tasks filtered by status.
Tasks have a title, description, due date, priority, and status.
"""

response = openai.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": user_description},
    ],
    temperature=0.2,
)

spec_yaml = response.choices[0].message.content
spec = yaml.safe_load(spec_yaml)
print(yaml.dump(spec, sort_keys=False))

The temperature parameter is kept low to reduce creative drift and keep output deterministic. The system prompt enforces structure, but you should always validate the result.

Approach 2: Structured Output with JSON Schema

Modern LLM APIs support structured outputs, which guarantee the response conforms to a JSON schema. You can define a schema matching the OpenAPI structure and let the model fill it in.

from openai import OpenAI
import json

client = OpenAI()

openapi_schema = {
    "type": "object",
    "properties": {
        "openapi": {"type": "string", "const": "3.1.0"},
        "info": {
            "type": "object",
            "properties": {
                "title": {"type": "string"},
                "version": {"type": "string"},
                "description": {"type": "string"},
            },
            "required": ["title", "version"],
        },
        "paths": {
            "type": "object",
            "additionalProperties": {
                "type": "object",
                "additionalProperties": {"type": "object"},
            },
        },
    },
    "required": ["openapi", "info", "paths"],
}

response = client.chat.completions.create(
    model="gpt-4o-2024-08-06",
    messages=[
        {"role": "system", "content": "Generate an OpenAPI spec from the user's description."},
        {"role": "user", "content": "A weather API with endpoints for current conditions and 7-day forecasts by city."},
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "openapi_spec",
            "strict": True,
            "schema": openapi_schema,
        },
    },
)

spec = json.loads(response.choices[0].message.content)
print(json.dumps(spec, indent=2))

This approach eliminates most formatting errors because the model is constrained to the schema. However, deeply nested OpenAPI structures can be complex to fully express in a JSON schema, so you may need to post-process or merge partial results.

Approach 3: Using Specialized Libraries

Several open-source libraries wrap the LLM prompting logic and add validation, formatting, and refinement steps. One popular pattern is a multi-step pipeline: generate, validate, fix, and return.

from openai import OpenAI
import yaml
import json
from jsonschema import validate, ValidationError

client = OpenAI()

OPENAPI_31_META_SCHEMA_URL = "https://spec.openapis.org/oas/3.1/schema/2022-10-07"

def generate_spec(description: str) -> dict:
    """Generate an OpenAPI spec from a natural language description."""
    prompt = f"""Generate a complete OpenAPI 3.1 specification in YAML for:
{description}

Include paths, components/schemas, request bodies, and responses.
Output only YAML."""

    resp = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "You are an expert API designer."},
            {"role": "user", "content": prompt},
        ],
        temperature=0.1,
    )
    return yaml.safe_load(resp.choices[0].message.content)

def fix_spec(spec: dict, errors: str) -> dict:
    """Ask the LLM to fix validation errors."""
    resp = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "Fix the OpenAPI spec errors. Output only YAML."},
            {"role": "user", "content": f"Spec:\n{yaml.dump(spec)}\n\nErrors:\n{errors}"},
        ],
        temperature=0.1,
    )
    return yaml.safe_load(resp.choices[0].message.content)

def generate_validated_spec(description: str, max_retries: int = 3) -> dict:
    spec = generate_spec(description)
    for attempt in range(max_retries):
        try:
            # Basic structural checks
            assert spec.get("openapi", "").startswith("3.")
            assert "info" in spec
            assert "paths" in spec
            return spec
        except (AssertionError, ValidationError) as e:
            spec = fix_spec(spec, str(e))
    return spec

spec = generate_validated_spec(
    "An e-commerce API for products, cart, and checkout with Stripe integration."
)
print(yaml.dump(spec, sort_keys=False))

This pipeline pattern — generate, validate, repair — is the backbone of production-grade natural language to OpenAPI tools. It dramatically reduces the rate of invalid specs reaching your codebase.

Approach 4: Using Swagger Editor AI and Cloud Tools

Several SaaS platforms now offer built-in AI generation. SwaggerHub, Stoplight, and Postman all include natural language features. For programmatic use, you can also leverage open-source projects like openapi-ai:

# Install the CLI tool
# npm install -g @openapi-ai/cli

# Generate a spec from a text file
# openapi-ai generate --input api-description.txt --output openapi.yaml

# Or pipe directly
# echo "A blog API with posts, comments, and authors" | openapi-ai generate > openapi.yaml

These tools handle validation, schema reuse, and even documentation generation in one step, making them suitable for teams that want a turnkey solution.

Best Practices

Write Clear, Structured Prompts

The quality of the generated spec depends heavily on the input description. Vague prompts produce vague APIs. Include the following in your description:

# Weak prompt
"Make a user API"

# Strong prompt
"Design a REST API for user management. Resources: users (id, email,
name, role, created_at) and user_sessions (id, user_id, token, expires_at).
Operations: list users with pagination (limit, offset), get user by id,
create user, update user, delete user, revoke session. Authentication:
Bearer JWT. Use standard HTTP status codes. Wrap responses in
{ data, meta, error } envelope."

Always Validate the Output

Never trust generated specs blindly. Use a validator like openapi-spec-validator or spectral to catch structural and style issues:

# Install validators
# pip install openapi-spec-validator
# npm install -g @stoplight/spectral-cli

from openapi_spec_validator import validate_spec
from openapi_spec_validator.readers import read_from_filename

spec_dict, _ = read_from_filename("openapi.yaml")
try:
    validate_spec(spec_dict)
    print("Spec is valid!")
except Exception as e:
    print(f"Validation failed: {e}")

# Run Spectral for linting
# spectral lint openapi.yaml

Iterate with Refinement Prompts

Treat generation as a conversation. After the initial spec, ask the model to refine specific sections:

refinement_prompt = """Take this OpenAPI spec and make these changes:
1. Add rate limiting headers (X-RateLimit-Limit, X-RateLimit-Remaining).
2. Add a 429 response to all list endpoints.
3. Rename 'User' schema to 'UserDTO' and add a 'UserCreate' schema
   for POST request bodies.
4. Add examples to all schemas.

Spec:
""" + yaml.dump(spec)

This iterative approach produces specs that match your team's conventions far better than a single-shot generation.

Use a Style Guide

Pair generation with a linting ruleset like spectral:oas or a custom ruleset enforcing your organization's conventions. Run the linter after every generation and feed errors back into the fix loop.

# .spectral.yaml
extends: spectral:oas
rules:
  oas3-operation-security-defined: error
  operation-tags: error
  operation-operationId: error
  info-contact: off
  naming-convention:
    severity: error
    given: "$.paths.*[get,post,put,patch,delete]"
    then:
      field: operationId
      function: pattern
      functionOptions:
        match: "^[a-z]+[A-Za-z0-9]+$"

Version and Store Generated Specs

Generated specs should live in version control alongside your application code. Treat them as source artifacts, not generated throwaways. This enables code review, diffing, and rollback when the AI produces something unexpected.

Keep Humans in the Loop

AI generation accelerates scaffolding but cannot replace human judgment on security, naming, and domain modeling. Always have a developer review the spec before it becomes the contract for implementation. Pay special attention to:

Conclusion

Generating OpenAPI specifications from natural language transforms one of the most repetitive tasks in API development into a fast, iterative, and collaborative process. By combining clear prompting, structured output constraints, validation pipelines, and human review, teams can produce high-quality API contracts in minutes rather than hours. The technology is not a replacement for thoughtful API design, but it is a powerful accelerator that lets developers focus on semantics and strategy instead of YAML syntax. As LLMs and specialized tooling continue to mature, natural language to OpenAPI generation will become a standard step in every API-first workflow.

— Ad —

Google AdSense will appear here after approval

← Back to all articles