Convert REST API Response JSON to Python Classes

Learn how to convert a complete REST API response with metadata, pagination, and nested data into a hierarchy of Python dataclasses or Pydantic models.

Advanced Patterns

Detailed Explanation

Modeling a Full API Response in Python

Production API responses include metadata, pagination, error envelopes, and nested resources. Converting these into Python types requires a systematic approach.

Example JSON

{
  "status": "ok",
  "data": {
    "users": [
      {"id": 1, "name": "Alice", "email": "alice@example.com"},
      {"id": 2, "name": "Bob", "email": "bob@example.com"}
    ]
  },
  "pagination": {
    "page": 1,
    "per_page": 20,
    "total": 152,
    "total_pages": 8
  }
}

Generated Python (Dataclass)

from dataclasses import dataclass

@dataclass
class User:
    id: int
    name: str
    email: str

@dataclass
class UserData:
    users: list[User]

@dataclass
class Pagination:
    page: int
    per_page: int
    total: int
    total_pages: int

@dataclass
class ApiResponse:
    status: str
    data: UserData
    pagination: Pagination

Generated Python (Pydantic)

from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str
    email: str

class UserData(BaseModel):
    users: list[User]

class Pagination(BaseModel):
    page: int
    per_page: int
    total: int
    total_pages: int

class ApiResponse(BaseModel):
    status: str
    data: UserData
    pagination: Pagination

Generic Response Wrapper

For APIs that use the same envelope for every endpoint:

from typing import Generic, TypeVar

T = TypeVar("T")

@dataclass
class ApiResponse(Generic[T]):
    status: str
    data: T
    pagination: Pagination

# Usage
response: ApiResponse[UserData] = ...

Error Handling

APIs return different shapes for errors:

from typing import Optional

@dataclass
class ApiError:
    code: str
    message: str

@dataclass
class ApiResponse(Generic[T]):
    status: str
    data: Optional[T] = None
    pagination: Optional[Pagination] = None
    error: Optional[ApiError] = None

Deserialization with dacite

from dacite import from_dict

response = from_dict(data_class=ApiResponse, data=json.loads(raw_json))
for user in response.data.users:
    print(user.name)

Best Practices

  1. Define leaf types firstUser, then UserData, then ApiResponse.
  2. Extract reusable typesPagination is shared across endpoints.
  3. Use generics for wrappers — Avoid duplicating the envelope for each resource.

Use Case

You are building a Python SDK for a REST API and need a complete type hierarchy that models success responses, error responses, and pagination metadata for every endpoint.

Try It — JSON to Python Converter

Open full tool