Skip to content

Custom Media Types

Generate valid payloads for media types that Schemathesis doesn't recognize by default.

When to use custom media types

Use custom media type strategies when your API accepts content that Schemathesis can't generate automatically:

  • Binary formats - PDFs, images, audio files, or proprietary formats
  • Specialized text formats - Custom configuration files, or domain-specific formats

Quick Start: PDF File Upload

Problem: Your API accepts PDF uploads, but Schemathesis generates invalid binary data that fails validation.

Register a strategy that generates valid PDF headers:

# pdf_strategy.py
from hypothesis import strategies as st
import schemathesis

pdf_strategy = st.sampled_from(
    [
        b"%PDF-1.4\n1 0 obj\n<<\n/Type /Catalog\n/Pages 2 0 R\n>>\nendobj\nxref\n0 3\n0000000000 65535 f \ntrailer\n<<\n/Size 3\n/Root 10 R\n>>\nstartxref\n9\n%%EOF",
        b"%PDF-1.5\n%\xe2\xe3\xcf\xd3\n1 0 obj\n<<\n/Type /Catalog\n/Pages 2 0 R\n>>\nendobj\nxref\n0 3\n0000000000 65535 f \ntrailer\n<<\n/Size 3\n/Root 1 0 R\n>>\nstartxref\n9\n%%EOF",
    ]
)
# Register the strategy for PDF media type
schemathesis.openapi.media_type("application/pdf", pdf_strategy)
export SCHEMATHESIS_HOOKS=pdf_strategy
schemathesis run http://localhost:8000/openapi.json

Result: File upload endpoints receive valid PDF content instead of random bytes.

Media Type Matching

The media type string you register must exactly match the one defined in your OpenAPI specification. For example, if your spec uses application/pdf, register exactly application/pdf - not application/x-pdf or any other variation. Check your OpenAPI schema's requestBody.content section to find the exact media type strings.

Common Media Type Patterns

Image files

image_strategy = st.sampled_from(
    [
        # Minimal PNG
        b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\nIDAT\x08\x1dc\xf8\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00IEND\xaeB`\x82",
        # Minimal JPEG
        b"\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x00H\x00H\x00\x00\xff\xdb\x00C\x00\x08\x06\x06\x07\x06\x05\x08\x07\x07\x07\t\t\x08\n\x0c\x14\r\x0c\x0b\x0b\x0c\x19\x12\x13\x0f\x14\x1d\x1a\x1f\x1e\x1d\x1a\x1c\x1c $.' \",#\x1c\x1c(7),01444\x1f'9=82<.342\xff\xc0\x00\x11\x08\x00\x01\x00\x01\x01\x01\x11\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x14\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x00?\x00\xff\xd9",
    ]
)

schemathesis.openapi.media_type("image/*", image_strategy)

Archives

import zipfile
import io

def create_test_zip():
    """Create a minimal valid ZIP file"""
    buffer = io.BytesIO()
    with zipfile.ZipFile(buffer, 'w') as zf:
        zf.writestr("test.txt", "test content")
    return buffer.getvalue()

zip_strategy = st.just(create_test_zip())
schemathesis.openapi.media_type("application/zip", zip_strategy)

Media Type Aliases

# Register PDF strategy with common aliases
schemathesis.openapi.media_type(
    "application/pdf", 
    pdf_strategy,
    aliases=["application/x-pdf", "application/acrobat"]
)

# Handles application/pdf, application/x-pdf, and application/acrobat

Dynamic Content Generation

from hypothesis import strategies as st

@st.composite
def dynamic_xml(draw):
    """Generate XML with random but valid structure"""
    tag_name = draw(st.text(
        alphabet=st.characters(
            whitelist_categories=["L"]), min_size=3, max_size=10
        )
    )
    content = draw(st.text(min_size=1, max_size=50))

    return f"<?xml version='1.0'?><{tag_name}>{content}</{tag_name}>".encode()

schemathesis.openapi.media_type("application/xml", dynamic_xml())