Skip to content

Field Types

Complete guide to Airtable fields and field type detection.


Field Type Detection Overview

Pydantic Airtable automatically detects the appropriate Airtable field type based on:

  1. Field name patterns - Keywords in the field name
  2. Python type - The type annotation
  3. Explicit override - Using airtable_field()

Detection Priority

When determining field type, the library checks in this order:

  1. Explicit type - airtable_field(field_type=...) always wins
  2. Field metadata - Existing type in field's json_schema_extra
  3. Field name patterns - Field type detection from naming
  4. Python type - Default mapping from type annotation

Field Name Patterns

Email Fields

Field names matching these patterns become EMAIL fields:

  • email
  • e_mail
  • mail
  • contact
class User(BaseModel):
    email: str              # → EMAIL
    contact_email: str      # → EMAIL
    user_mail: str          # → EMAIL

URL Fields

Field names matching these patterns become URL fields:

  • url
  • link
  • website
  • site
  • href
class Company(BaseModel):
    website: str            # → URL
    linkedin_url: str       # → URL
    portfolio_link: str     # → URL

Phone Fields

Field names matching these patterns become PHONE fields:

  • phone
  • tel
  • mobile
  • cell
class Contact(BaseModel):
    phone: str              # → PHONE
    mobile_number: str      # → PHONE
    work_tel: str           # → PHONE

Long Text Fields

Field names matching these patterns become LONG_TEXT (multiline) fields:

  • description
  • comment
  • note
  • bio
  • summary
  • content
  • body
  • message
  • detail
class Article(BaseModel):
    description: str        # → LONG_TEXT
    content: str            # → LONG_TEXT
    author_bio: str         # → LONG_TEXT
    notes: str              # → LONG_TEXT

Currency Fields

Number fields with these name patterns become CURRENCY fields:

  • price
  • cost
  • amount
  • fee
  • salary
  • wage
  • revenue
  • budget
  • payment
class Product(BaseModel):
    price: float            # → CURRENCY
    shipping_cost: float    # → CURRENCY
    total_amount: float     # → CURRENCY

Percentage Fields

Number fields with these name patterns become PERCENT fields:

  • percent
  • percentage
  • rate
  • ratio
class Metrics(BaseModel):
    completion_rate: float  # → PERCENT
    success_percentage: float  # → PERCENT
    conversion_ratio: float # → PERCENT

Python Type Mappings

Basic Types

Python Type Airtable Type
str SINGLE_LINE_TEXT
int NUMBER
float NUMBER
bool CHECKBOX

Date/Time Types

Python Type Airtable Type
datetime DATETIME
date DATE
timedelta DURATION

Other Types

Python Type Airtable Type Notes
int (with rating pattern) RATING For fields named rating, stars, score, rank

Special Types (Explicit Only)

These field types require explicit specification - they are not auto-detected:

Airtable Type Description
LINKED_RECORD Links to records in another table
USER Collaborator/user references
BUTTON Triggers automations (read-only)
BARCODE Stores barcode text

AUTO_NUMBER Field Limitation

The Airtable public API does not support creating fields with type AUTO_NUMBER. If you need an auto-incrementing number field, you must:

  1. Create a NUMBER field via the API or model
  2. Manually convert it to Auto number in the Airtable UI (Field settings → Convert to Auto number)

Once converted, the field becomes read-only and auto-increments for new records.

Complex Types

Python Type Airtable Type
Enum SELECT
List[str] MULTI_SELECT

All Airtable Field Types

The library supports these Airtable field types:

from pydantic_airtable import AirtableFieldType

class AirtableFieldType(str, Enum):
    SINGLE_LINE_TEXT = "singleLineText"
    LONG_TEXT = "multilineText"
    NUMBER = "number"
    CURRENCY = "currency"
    PERCENT = "percent"
    DATE = "date"
    DATETIME = "dateTime"
    DURATION = "duration"
    RATING = "rating"
    CHECKBOX = "checkbox"

    # Relational fields
    LINKED_RECORD = "multipleRecordLinks"

    # Special fields
    BARCODE = "barcode"
    BUTTON = "button"
    USER = "multipleCollaborators"
    SELECT = "singleSelect"
    MULTI_SELECT = "multipleSelects"
    EMAIL = "email"
    URL = "url"
    PHONE = "phoneNumber"
    ATTACHMENT = "multipleAttachments"
    FORMULA = "formula"
    ROLLUP = "rollup"
    COUNT = "count"
    LOOKUP = "lookup"
    CREATED_TIME = "createdTime"
    MODIFIED_TIME = "lastModifiedTime"
    CREATED_BY = "createdBy"
    MODIFIED_BY = "lastModifiedBy"
    AUTO_NUMBER = "autoNumber"

Overriding Detection

Use airtable_field() to override automatic detection:

Explicit Field Type

from pydantic_airtable import airtable_field, AirtableFieldType

@airtable_model(table_name="Products")
class Product(BaseModel):
    name: str

    # Override: 'code' would normally be SINGLE_LINE_TEXT
    code: str = airtable_field(
        field_type=AirtableFieldType.SINGLE_LINE_TEXT
    )

    # Override: 'notes' detected as LONG_TEXT, but we want single line
    notes: str = airtable_field(
        field_type=AirtableFieldType.SINGLE_LINE_TEXT
    )

Custom Field Name

Map Python field name to different Airtable field name:

@airtable_model(table_name="Users")
class User(BaseModel):
    # Python: 'full_name' → Airtable: 'Full Name'
    full_name: str = airtable_field(
        field_name="Full Name"
    )

    # Python: 'desc' → Airtable: 'Description'
    desc: str = airtable_field(
        field_name="Description",
        field_type=AirtableFieldType.LONG_TEXT
    )

Select Field with Choices

@airtable_model(table_name="Tasks")
class Task(BaseModel):
    title: str

    status: str = airtable_field(
        field_type=AirtableFieldType.SELECT,
        choices=["To Do", "In Progress", "Review", "Done"]
    )

    tags: List[str] = airtable_field(
        field_type=AirtableFieldType.MULTI_SELECT,
        choices=["Frontend", "Backend", "Database", "DevOps"]
    )

Read-Only Fields

Mark computed or system fields as read-only:

@airtable_model(table_name="Records")
class Record(BaseModel):
    name: str

    # Won't be sent during create/update
    record_number: int = airtable_field(
        read_only=True
    )

    # Computed field
    formula_result: str = airtable_field(
        field_type=AirtableFieldType.FORMULA,
        read_only=True
    )

Field Options

Different field types support different options:

Checkbox Options

# Default: green checkmark
is_active: bool  # icon="check", color="greenBright"

Currency Options

price: float = airtable_field(
    field_type=AirtableFieldType.CURRENCY,
    json_schema_extra={
        "precision": 2,      # Decimal places
        "symbol": "€"        # Currency symbol
    }
)

Percent Options

rate: float = airtable_field(
    field_type=AirtableFieldType.PERCENT,
    json_schema_extra={
        "precision": 1       # Decimal places
    }
)

Number Options

count: int = airtable_field(
    field_type=AirtableFieldType.NUMBER,
    json_schema_extra={
        "precision": 0       # Integer (no decimals)
    }
)

Working with Enums

Basic Enum

from enum import Enum

class Priority(str, Enum):
    LOW = "Low"
    MEDIUM = "Medium"
    HIGH = "High"

@airtable_model(table_name="Tasks")
class Task(BaseModel):
    title: str
    priority: Priority = Priority.MEDIUM

Enum with Values

Airtable displays the enum value, not the name:

class Status(str, Enum):
    DRAFT = "📝 Draft"
    REVIEW = "👀 Under Review"
    APPROVED = "✅ Approved"
    REJECTED = "❌ Rejected"

# In Airtable, you'll see: "📝 Draft", "👀 Under Review", etc.

Optional Enum

class Category(str, Enum):
    TECH = "Technology"
    BUSINESS = "Business"
    PERSONAL = "Personal"

@airtable_model(table_name="Tasks")
class Task(BaseModel):
    title: str
    category: Optional[Category] = None  # Can be unset

Detection Examples

Here's a comprehensive example showing various detections:

from pydantic_airtable import airtable_model, airtable_field, AirtableFieldType
from pydantic import BaseModel
from typing import Optional, List
from datetime import datetime
from enum import Enum

class Priority(str, Enum):
    LOW = "Low"
    MEDIUM = "Medium"
    HIGH = "High"

@airtable_model(table_name="Contacts")
class Contact(BaseModel):
    # Auto-detected types
    name: str                      # → SINGLE_LINE_TEXT
    email: str                     # → EMAIL
    phone: str                     # → PHONE
    website: Optional[str] = None  # → URL
    bio: Optional[str] = None      # → LONG_TEXT
    age: Optional[int] = None      # → NUMBER
    salary: Optional[float] = None # → CURRENCY
    is_active: bool = True         # → CHECKBOX
    created_at: datetime           # → DATETIME
    priority: Priority             # → SELECT

    # Explicit overrides
    internal_code: str = airtable_field(
        field_name="Internal Code",
        field_type=AirtableFieldType.SINGLE_LINE_TEXT
    )

    tags: List[str] = airtable_field(
        field_type=AirtableFieldType.MULTI_SELECT,
        choices=["VIP", "Partner", "Lead", "Customer"]
    )

Troubleshooting

Field Type Not Detected

If a field isn't detected as expected:

# Problem: 'status' isn't detected as SELECT
status: str  # → SINGLE_LINE_TEXT

# Solution: Use explicit type
status: str = airtable_field(
    field_type=AirtableFieldType.SELECT,
    choices=["Active", "Inactive"]
)

Wrong Type Detected

# Problem: 'rate' detected as PERCENT but you want NUMBER
rate: float  # → PERCENT (because of 'rate' in name)

# Solution: Override
rate: float = airtable_field(
    field_type=AirtableFieldType.NUMBER
)

Field Name Conflict

# Problem: Python name differs from Airtable name
# Airtable has "First Name" but Python can't use spaces

first_name: str = airtable_field(
    field_name="First Name"
)

Next Steps