FastAPI Return Null: Best Practices for API Design
In the vast and interconnected landscape of modern software development, Application Programming Interfaces (APIs) serve as the fundamental connective tissue, enabling disparate systems to communicate, share data, and collaborate seamlessly. From powering mobile applications and sophisticated front-end frameworks to orchestrating complex microservices architectures and facilitating B2B integrations, APIs are the invisible workhorses that drive innovation and efficiency. The quality of an API's design, therefore, is not merely an aesthetic concern but a critical determinant of its usability, maintainability, and ultimately, its success. A well-designed api acts as a clear contract, reducing friction for developers and accelerating the pace of development. Conversely, poorly designed APIs can introduce ambiguity, errors, and significant overhead for both producers and consumers, leading to frustration and increased operational costs. Among the myriad design considerations, one seemingly innocuous detail — the handling of "null" or "empty" values in API responses — often becomes a surprisingly complex source of confusion and inconsistency.
FastAPI, a modern, fast (high-performance) web framework for building APIs with Python 3.7+ based on standard Python type hints, has rapidly gained popularity due to its exceptional performance, automatic interactive OpenAPI documentation (Swagger UI and ReDoc), and intuitive developer experience. Its reliance on Pydantic for data validation and serialization/deserialization, combined with Python's robust type hinting system, provides a powerful foundation for building robust and well-defined APIs. However, even with FastAPI's sophisticated tooling, the responsibility for making informed decisions about how to represent the absence of data — whether through an explicit null, an empty collection, an empty object, or the complete omission of a field — ultimately falls to the API designer. These decisions have far-reaching implications, affecting everything from client-side parsing logic and data storage to the overall coherence of an API's contract and the effectiveness of its API Governance strategy. This comprehensive exploration delves into the nuances of FastAPI return null scenarios, providing a detailed guide to best practices for designing APIs that gracefully handle absent data, ensuring clarity, consistency, and a superior developer experience. We will dissect the semantic distinctions between various "empty" states, examine FastAPI's mechanisms for handling them, and articulate a set of principles for making deliberate design choices, all while emphasizing the crucial role of OpenAPI specifications and robust API Governance in maintaining a predictable and reliable API ecosystem.
Understanding "Null" and its Nuances in API Responses
Before diving into specific implementation strategies within FastAPI, it is imperative to establish a clear conceptual understanding of what "null" signifies in the context of API responses and how it differs from other forms of "emptiness." The term "null" inherently implies the absence of a value. In many programming languages and data formats like JSON, null is a distinct primitive type used to explicitly denote that a variable or field has no value. However, the interpretation and implications of null can vary significantly depending on the context and the explicit contract defined by the api.
Consider the following distinctions, which are often conflated but carry unique semantic implications:
null: This explicitly signifies that a field exists in the data structure, but it currently holds no value. It's an intentional statement of absence. For example, aUserobject might have abiofield. If a user has not provided a biography,bio: nullclearly communicates that the field is present in the schema but its value is currently empty. The client consuming thisapiknows to expect abiofield, even if its value isnull.- Empty String (
""): An empty string is a valid string value, not the absence of a value. Semantically, it means "a string with zero characters." While often used interchangeably withnullin some informal contexts, they are fundamentally different. If afirst_namefield is an empty string, it suggests the user's first name exists but is currently an empty textual value, which might indicate a data entry error or a specific business rule. Contrast this withfirst_name: null, which would imply the first name is completely unknown or unassigned. Client-side logic for an empty string might involve displaying nothing or a placeholder, whereas fornull, it might involve checking if the field has been set at all. - Empty Array (
[]): An empty array represents a collection that currently contains no elements. It explicitly states that a list or collection is present, but it is devoid of items. For instance, aProductobject might have atagsfield, which is a list of strings. If a product has no tags,tags: []is the most semantically correct and convenient representation. It tells the client thattagsis indeed a list, just an empty one, allowing for straightforward iteration without needing to check fornullbefore looping. Returningtags: nullin this scenario could imply that thetagscollection itself is not present or not applicable, which is usually not the intended meaning for an empty list. - Empty Object (
{}): An empty object signifies a complex data structure that exists but has no properties within it. For example, anAddressobject might be a sub-object within aUserprofile. If a user has an address field but no specific details (street, city, zip) have been provided, returningaddress: {}could indicate that the address structure is present but empty. This is often less common than empty arrays or nulls for scalar values, as it can sometimes introduce ambiguity about the schema's expected properties. A more common approach for an entirely absent address might beaddress: nullor even omitting the field. - Missing Fields: This is perhaps the most nuanced scenario. If a field is entirely absent from the JSON response, it implies that the field was either never intended to be part of the response under the current conditions, or it was explicitly omitted because its value was
nulland the API design dictates thatnullfields should not be included. The key difference fromnullis that the field name itself does not appear in the payload. Clients consuming such anapimust be prepared to handle missing fields gracefully, often by providing default values or conditional rendering logic. Whilenullexplicitly states "no value," a missing field implicitly states "no value, and by the way, I'm not even sending the key for it."
The significance of these distinctions cannot be overstated. From a client developer's perspective, inconsistent handling of these "empty" states can lead to brittle code, unexpected errors, and increased debugging time. If an API sometimes returns null for an empty list and sometimes [], the client must implement dual checks, complicating parsing logic. Similarly, an API that sometimes omits a field when its value is null and sometimes explicitly returns null for the same field creates an unpredictable contract. The choice made by the api producer fundamentally shapes the consumption experience. Therefore, a deliberate and consistent approach, clearly articulated through robust API Governance and precise OpenAPI specifications, is essential for building an api that is both powerful and developer-friendly.
FastAPI's Handling of Nulls and Optional Types
FastAPI, built upon Python's type hinting and Pydantic, provides an incredibly powerful and flexible mechanism for defining data structures and validating payloads. This system inherently influences how "null" values are handled both in API requests and responses, and how these behaviors are reflected in the generated OpenAPI documentation.
At the heart of FastAPI's data handling lies Pydantic, a data validation and settings management library using Python type annotations. Pydantic models are the schema definitions for your API's input and output data. When you define a field in a Pydantic model, you explicitly state its type.
For handling potentially absent values, Python's type hinting, specifically the Optional type (or its modern equivalent, Union[Type, None]), becomes crucial.
- Default Values in Pydantic Models: You can explicitly set default values for fields. For optional fields, setting
Noneas the default explicitly indicates that if a value is not provided during instantiation, it should beNone. For non-optional fields, a default value ensures the field always has a value, preventing it from beingnullunless explicitly assignedNone(which would typically contradict its non-optional type hint). - How FastAPI Renders these in
OpenAPISpecifications: One of FastAPI's most powerful features is its automatic generation ofOpenAPI(Swagger/ReDoc) documentation based on your Pydantic models and path operations. When you useOptional[Type](orType | None), FastAPI translates this into theOpenAPIschema by adding"nullable": trueto the field's definition. This explicitly informs API consumers, and tools that processOpenAPIspecifications, that the field can legally contain anullvalue. ForList[Type]fields, theOpenAPIschema will show the type as an array, and if a default empty list is provided, this often doesn't explicitly marknullable, implying that an empty array[]is the expected "empty" state.ForUserProfilemodel, theOpenAPIschema would typically show something like:json "UserProfile": { "title": "UserProfile", "type": "object", "properties": { "username": { "title": "Username", "type": "string" }, "email": { "title": "Email", "type": "string", "nullable": true // Explicitly marked as nullable }, "bio": { "title": "Bio", "type": "string", "nullable": true, // Explicitly marked as nullable "example": "A passionate software engineer." }, "tags": { "title": "Tags", "type": "array", "items": { "type": "string" }, "default": [], "example": ["python", "fastapi"] }, "profile_picture_url": { "title": "Profile Picture Url", "type": "string", "nullable": true } }, "required": ["username"] // Only username is required } - The
exclude_noneorexclude_unsetOptions: Pydantic's.model_dump()(or.dict()for older versions) and.model_dump_json()methods, as well as FastAPI'sjsonable_encoderutility, offer powerful options to control the serialization process.Consider theuser_no_bioexample again, but withexclude_none=True:python print(user_no_bio.model_dump_json(exclude_none=True, indent=2))Output withexclude_none=True:json { "username": "jane_doe", "email": "jane@example.com", "tags": [] }Notice thatbioandprofile_picture_urlare now completely omitted because their values wereNone. Thetagsfield, even though empty, is still included because[]is notNone.exclude_none=True: This will omit fields from the JSON output if their value isNone. This can significantly reduce the payload size if many optional fields are frequentlyNone.exclude_unset=True: This goes a step further. It omits fields that were not explicitly set during model instantiation and do not have a default value. This is particularly useful for partial updates (PATCH requests) where you only want to send the fields that were actually provided by the client.
- Trade-offs of Explicit
nullvs. Omitting Fields:- Explicit
null(default FastAPI/Pydantic behavior forOptional):- Pros: Clearly communicates that a field exists in the schema but currently has no value. Clients can reliably expect the field to be present, simplifying parsing. Aligns well with
OpenAPI'snullable: trueexplicit declaration. - Cons: Can increase payload size if many optional fields are often
null. Some client libraries might handle missing fields more gracefully thannullvalues, requiring explicitnullchecks.
- Pros: Clearly communicates that a field exists in the schema but currently has no value. Clients can reliably expect the field to be present, simplifying parsing. Aligns well with
- Omitting Fields (
exclude_none=True):- Pros: Reduces payload size, which can be beneficial for performance, especially over mobile networks. Can simplify client-side logic if the client only cares about fields that actually have a value.
- Cons: The absence of a field can be ambiguous. Does it mean "not applicable," "not provided," or "value is
nullbut omitted"? Clients must be prepared to check for the presence of a key, not just its value. This can makeapicontracts less explicit and potentially harder to debug if the client expects a field that is sometimes omitted.
- Explicit
Optional[Type] (or Type | None): This type hint signals that a field may or may not have a value of Type. If no value is provided, it defaults to None in Python. When Pydantic serializes this to JSON, if the field's value is None, it will typically be rendered as null.Let's illustrate with an example:```python from typing import Optional, List from pydantic import BaseModel, Fieldclass UserProfile(BaseModel): username: str email: Optional[str] = None # Optional field, defaults to None bio: Optional[str] = Field(None, example="A passionate software engineer.") tags: List[str] = Field([], example=["python", "fastapi"]) # List defaults to empty list profile_picture_url: Optional[str] = None
Example of a user profile with some optional fields not set
user_no_bio = UserProfile(username="jane_doe", email="jane@example.com") print(user_no_bio.model_dump_json(indent=2)) ```Output for user_no_bio: json { "username": "jane_doe", "email": "jane@example.com", "bio": null, "tags": [], "profile_picture_url": null }In this example, email, bio, and profile_picture_url are Optional[str]. If they are not provided during model instantiation, Pydantic assigns None to them, which then serializes to null in the JSON response. The tags field is a List[str] and defaults to an empty list [], correctly serializing as such.
FastAPI's flexible mechanisms, combined with Pydantic's powerful type system, offer the tools needed to manage null and empty states effectively. However, the choice of strategy requires careful consideration and must be consistent across your api to avoid confusion. This consistency is a cornerstone of good API Governance and is precisely what we will explore in the following section on best practices.
Best Practices for API Design Regarding Nulls
Designing an API is not just about functionality; it's about crafting a clear, predictable, and delightful experience for the developers who will consume it. When it comes to handling null and various "empty" states, deliberate design choices are paramount. These choices, when consistently applied and clearly documented, form the bedrock of robust API Governance and ensure that your api remains understandable and maintainable over time.
Principle 1: Consistency is Key (API Governance)
Perhaps the most critical principle in API design, especially concerning null values, is consistency. An API that handles similar data types or scenarios in different ways is an API that breeds confusion and errors.
- Establish Clear Rules: Before writing any code, define clear, organization-wide standards for how
null, empty strings, empty arrays, empty objects, and missing fields will be used. For instance, decide:- Will
nullalways mean "no value for an optional field"? - Will empty lists always be
[]and nevernull? - When is it acceptable to omit a field entirely?
- Example: If you have a
Userresource where theprofile_picture_urlfield might not always be present, establish whether it will consistently returnprofile_picture_url: nullor if the field will be omitted (exclude_none=True). Whichever choice is made, it must be applied uniformly across all endpoints that return similar optional URL fields. Avoid returningnullin one endpoint and omitting the field in another for the same semantic meaning.
- Will
- Document These Rules Thoroughly (
OpenAPI): TheOpenAPIspecification is your contract. Use it to explicitly document these decisions. If a field can benull, mark it withnullable: true. If an array can be empty, ensure its schema clearly indicates it's an array type. Provide example values that demonstrate both populated and "empty" states (e.g., an exampleUserwith anullbio and another with a populated bio). This living documentation is crucial forAPI Governance, ensuring that all teams building or consuming theapiare aligned. - Avoid Mixing Approaches: For analogous situations, stick to one chosen method. For example, if
nullsignifies an unassigned value fordate_of_birth, do not use an empty string ("") for an unassignedjob_title. The semantic meaning of "unassigned" should map to a consistent representation.
Principle 2: Semantic Clarity
The representation of "empty" states should clearly convey their semantic meaning. Each choice—null, "", [], {}, or omission—carries a distinct message.
- Use
nullfor True Absence/Unknown Scalar Values:- When a field is optional and represents a single scalar value (string, number, boolean) that is truly absent, unknown, or not applicable,
nullis the most appropriate choice. - Example: A
shipping_address_line2in anOrderobject might benullif the customer only has a single address line. Adiscount_codemight benullif no discount was applied.nullexplicitly tells the consumer, "this field exists, but its value is currently nothing."
- When a field is optional and represents a single scalar value (string, number, boolean) that is truly absent, unknown, or not applicable,
- Use Empty Arrays
[]for Empty Collections:- For fields that represent collections (lists, arrays), an empty array
[]is almost always preferred overnull. An empty array clearly indicates that the collection exists, it's just currently devoid of items. - Example: If a
Producthas no associatedreviews, returnreviews: []instead ofreviews: null. This allows client-side code to iterate overreviewsdirectly without needing to check fornullfirst, making the code cleaner and less error-prone. Optional[List[Type]]in Pydantic will allowNonefor the entire list. If your intent is that the list always exists but might be empty, thenList[Type]with a default of[]is the correct choice (tags: List[str] = []).
- For fields that represent collections (lists, arrays), an empty array
- Use Empty Objects
{}for Empty Nested Structures (with caution):- For nested objects,
address: {}could mean "an address structure exists but has no filled properties." This can sometimes be less clear thanaddress: null(meaning no address provided at all) or omitting the field entirely. - Recommendation: Generally, if a nested object is entirely optional and has no properties, either use
nullto signify its complete absence (Optional[AddressModel]) or omit the field ifexclude_none=Trueis applied andNoneis its value. Using{}should be reserved for cases where the structure of the object is always expected, but all its sub-properties are optional and currently unset.
- For nested objects,
- Consider Omitting Fields for Conditional Presence:
- If a field is only relevant under specific conditions and its value is
nullwhen those conditions are not met, omitting the field can reduce payload size and signal its irrelevance more strongly than an explicitnull. - Example: An
admin_tokenfield on aUserobject, which only exists if the user is an administrator. For non-admin users,admin_tokencould be omitted. This is best achieved in FastAPI by making the fieldOptionaland then usingexclude_none=Truein your response serialization.
- If a field is only relevant under specific conditions and its value is
- Distinguish "Not Found" (404) from "Found but Empty Data" (200 with appropriate payload):
- A common mistake is using
nullin a successful (2xx) response to indicate that a resource was not found. This conflates data absence with resource existence. - Example:
GET /users/123/orders: If user 123 exists but has no orders, return200 OKwith[](an empty array of orders).GET /users/999/orders: If user 999 does not exist, return404 Not Found.
- Use HTTP status codes to communicate the outcome of the request at a resource level, and the response body to communicate the state of the data within the resource.
- A common mistake is using
Principle 3: Consumer-Centric Design
API design should always prioritize the ease of consumption. Decisions about null handling directly impact the complexity of client-side code.
- Reduce Client-Side Boilerplate: Aim for a design that minimizes conditional checks (
if (data === null) ...,if (data === undefined) ...) on the client.- Returning
[]for empty lists allows clients to universally iterate withoutnullchecks. - Consistently returning
nullfor optional scalar fields means clients can reliably expect the field to exist and check its value, rather than needing to check for field presence first.
- Returning
- Anticipate Client Technology Needs: Consider how different client technologies (JavaScript, Swift, Kotlin, Java, Go) might interpret
nullor missing fields. Some languages have stronger type systems that might struggle withOptionalfields that are sometimesnulland sometimes omitted. JavaScript, for instance, is more forgiving but can lead toundefinederrors if not handled carefully. - The "Always Return the Field, Even If Null" vs. "Omit If Null" Debate:
- Always return the field (explicit
null): This is generally safer and more explicit. Theapicontract guarantees the field's presence, even if its value isnull. This simplifies client-side schema validation and parsing logic, as the client doesn't need to guess if a field should be there. This aligns well withOpenAPI'snullable: true. - Omit if
null(exclude_none=True): Can be beneficial for reducing payload size and can make the response cleaner by only showing relevant data. However, it places a higher burden on the client to correctly interpret the absence of a field. This approach needs to be clearly documented inOpenAPIwith appropriateexamplesand potentially a note aboutnullvalues being omitted. - Recommendation: For most general-purpose APIs, explicitly returning
nullfor optional fields (the default FastAPI/Pydantic behavior) is often the clearer and more robust choice, enhancing predictability. Omission should be a deliberate decision for specific scenarios (e.g., highly dynamic, sparse data, or where payload size is critically optimized).
- Always return the field (explicit
Principle 4: Explicit Documentation (OpenAPI)
The OpenAPI specification is the definitive source of truth for your API. It's not enough to make good design decisions; they must be clearly and unambiguously documented.
- Define
nullableFields inOpenAPI: As FastAPI automatically does withOptional[Type], ensure that any field that can legitimately benullis marked with"nullable": truein theOpenAPIschema. This is a critical piece of information for client SDK generators andapiconsumers. - Provide Example Values: Include realistic
examplevalues in yourOpenAPIschema that demonstrate various states:- An example of a populated field.
- An example where an optional field is
null. - An example where a list field is empty (
[]). - This helps developers quickly grasp the expected data shapes.
- Leverage FastAPI's Automatic Generation: FastAPI's ability to generate
OpenAPIautomatically from Pydantic models and path operations is invaluable. However, don't rely solely on defaults. UseField(..., example="...")to add examples, and potentiallyresponse_model_exclude_none=Truein path operations to control output based on your chosen strategy. If you choose to omit null fields, make sure yourOpenAPIexample reflects this omission.
Principle 5: Error Handling vs. Null Data
Maintain a clear separation between an expected absence of data (represented by null, [], or {}) and an error condition.
- Successful Request, No Data: If a client requests data that legitimately doesn't exist within an otherwise valid context (e.g., a search query with no matching results, a user account with no associated orders), return a
200 OKstatus code with an appropriate empty payload (e.g.,[]for lists, or an object withnullfields). Do not usenullin a 200 response to indicate an error or "resource not found." - Error Conditions: Use appropriate HTTP status codes (4xx for client errors, 5xx for server errors) for actual errors.
400 Bad Request: Invalid input.401 Unauthorized: Authentication required.403 Forbidden: Authenticated, but no permission.404 Not Found: The requested resource does not exist.500 Internal Server Error: Something went wrong on the server.- The response body for error conditions should typically follow a standardized error format (e.g., a JSON object with
code,message,details).
Principle 6: Versioning and Backward Compatibility
Changes in how null values are handled are breaking changes and must be managed carefully.
- Changing
nullbehavior is a breaking change: If yourapiinitially returnednullfor an empty list and you change it to[], clients expectingnullwill break. Similarly, if you start omitting fields that were previously returned asnull, clients expecting that field's presence will break. - Graceful Changes: When introducing such changes, use
apiversioning (e.g.,v1,v2in the URL path or anAcceptheader) to allow clients to migrate. Provide clear deprecation notices and migration guides.
Adhering to these principles, particularly those around consistency and semantic clarity, reinforces strong API Governance practices. This deliberate approach ensures that your API's contract is unambiguous, reducing the cognitive load on client developers and fostering a more stable and maintainable ecosystem.
APIPark is a high-performance AI gateway that allows you to securely access the most comprehensive LLM APIs globally on the APIPark platform, including OpenAI, Anthropic, Mistral, Llama2, Google Gemini, and more.Try APIPark now! 👇👇👇
Practical Examples in FastAPI
To solidify the understanding of these best practices, let's explore concrete examples within FastAPI, illustrating how to implement null and empty state handling effectively. These examples will demonstrate various scenarios and the corresponding Pydantic models and FastAPI responses.
Example 1: Optional Scalar Fields (User Profile)
A common scenario involves user profiles where certain pieces of information are optional. For instance, a user might not always provide a biography or a profile picture URL. In such cases, null is the appropriate way to signify the absence of that specific scalar value, while the field itself is expected to exist in the schema.
from typing import Optional, List
from pydantic import BaseModel, Field
from fastapi import FastAPI, HTTPException, status
from fastapi.responses import JSONResponse
app = FastAPI(
title="User Profile API",
description="An API for managing user profiles, demonstrating null handling.",
version="1.0.0",
)
class UserProfile(BaseModel):
id: str = Field(..., example="usr_12345")
username: str = Field(..., example="john.doe")
email: str = Field(..., example="john.doe@example.com")
full_name: Optional[str] = Field(None, example="John Doe", description="The user's full name, optional.")
bio: Optional[str] = Field(None, example="Passionate about technology and open source.", description="A short biography of the user, optional.")
profile_picture_url: Optional[str] = Field(None, example="https://example.com/profiles/john.jpg", description="URL to the user's profile picture, optional.")
last_login_ip: Optional[str] = Field(None, example="192.168.1.100", description="IP address of the last successful login, optional.")
# Here, `phone_numbers` is a list, and we'll ensure it defaults to an empty list, not null.
phone_numbers: List[str] = Field([], example=["+1-555-123-4567"], description="List of user's phone numbers, can be empty.")
# A mock database
db: List[UserProfile] = [
UserProfile(
id="usr_12345",
username="john.doe",
email="john.doe@example.com",
full_name="John Doe",
bio="Passionate about technology and open source.",
profile_picture_url="https://example.com/profiles/john.jpg",
phone_numbers=["+1-555-123-4567"]
),
UserProfile(
id="usr_67890",
username="jane.smith",
email="jane.smith@example.com",
# full_name, bio, profile_picture_url are intentionally omitted to show `null` behavior
last_login_ip="203.0.113.45"
),
UserProfile(
id="usr_11223",
username="alice.w",
email="alice.w@example.com",
full_name="Alice Wonderland",
phone_numbers=["+1-555-987-6543", "+1-555-111-2222"]
# bio, profile_picture_url, last_login_ip are intentionally omitted
)
]
@app.get("/users/{user_id}", response_model=UserProfile, summary="Retrieve a user profile")
async def get_user_profile(user_id: str):
"""
Fetches a user's profile by their unique ID.
Demonstrates how optional fields are returned as `null` if not set.
"""
for user in db:
if user.id == user_id:
return user
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
@app.get("/users_omit_null/{user_id}", response_model=UserProfile, response_model_exclude_none=True, summary="Retrieve a user profile, omitting null fields")
async def get_user_profile_omit_null(user_id: str):
"""
Fetches a user's profile, but configures the response to omit fields
that are `null` in the model, rather than returning them as `null`.
This demonstrates the `response_model_exclude_none=True` option.
"""
for user in db:
if user.id == user_id:
return user
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
# Example usage (run with `uvicorn your_script_name:app --reload`):
# GET /users/usr_12345
# Response for usr_12345 (full profile):
# {
# "id": "usr_12345",
# "username": "john.doe",
# "email": "john.doe@example.com",
# "full_name": "John Doe",
# "bio": "Passionate about technology and open source.",
# "profile_picture_url": "https://example.com/profiles/john.jpg",
# "last_login_ip": null,
# "phone_numbers": ["+1-555-123-4567"]
# }
# GET /users/usr_67890
# Response for usr_67890 (profile with many nulls):
# {
# "id": "usr_67890",
# "username": "jane.smith",
# "email": "jane.smith@example.com",
# "full_name": null,
# "bio": null,
# "profile_picture_url": null,
# "last_login_ip": "203.0.113.45",
# "phone_numbers": []
# }
# GET /users_omit_null/usr_67890
# Response for usr_67890 with omitted nulls:
# {
# "id": "usr_67890",
# "username": "jane.smith",
# "email": "jane.smith@example.com",
# "last_login_ip": "203.0.113.45",
# "phone_numbers": []
# }
As seen from get_user_profile, full_name, bio, profile_picture_url, and last_login_ip fields, when not provided, default to None in the Pydantic model and are correctly serialized as null in the JSON response, consistent with the nullable: true attribute in the OpenAPI specification. For phone_numbers, which is a list, it correctly defaults to [], an empty list, and is never null. The get_user_profile_omit_null endpoint demonstrates how setting response_model_exclude_none=True on a path operation instructs FastAPI to omit fields whose value is None during serialization, providing a cleaner, smaller payload at the cost of less explicit contract for the client.
Example 2: Empty Collections (Products in a Category)
When dealing with collections, the best practice is almost always to return an empty array [] rather than null if the collection exists but contains no items. This simplifies client-side logic significantly, as clients can always expect an iterable.
from typing import List, Optional
from pydantic import BaseModel, Field
from fastapi import FastAPI, HTTPException, status
app = FastAPI(
title="Product Catalog API",
description="Manages product categories and products.",
version="1.0.0",
)
class Product(BaseModel):
id: str = Field(..., example="prod_101")
name: str = Field(..., example="Wireless Mouse")
price: float = Field(..., example=25.99)
description: Optional[str] = Field(None, example="Ergonomic design with silent clicks.")
class Category(BaseModel):
id: str = Field(..., example="cat_electronics")
name: str = Field(..., example="Electronics")
description: Optional[str] = Field(None, example="Devices and gadgets for modern living.")
products: List[Product] = Field([], description="List of products in this category. Can be empty.")
# Another example of an optional scalar field
manager_email: Optional[str] = Field(None, example="manager@example.com", description="Email of the category manager, if assigned.")
# Mock data
categories_db: List[Category] = [
Category(
id="cat_electronics",
name="Electronics",
products=[
Product(id="prod_101", name="Wireless Mouse", price=25.99),
Product(id="prod_102", name="USB-C Hub", price=39.99, description="7-in-1 adapter.")
]
),
Category(
id="cat_books",
name="Books",
description="A world of stories and knowledge.",
products=[] # Category exists but has no products
),
Category(
id="cat_furniture",
name="Furniture",
# description and products are intentionally omitted for `cat_furniture` to show `null` description and empty products list
)
]
@app.get("/categories/{category_id}", response_model=Category, summary="Retrieve a category with its products")
async def get_category(category_id: str):
"""
Fetches a product category by its ID, including its associated products.
Demonstrates handling of empty product lists and optional scalar fields.
"""
for category in categories_db:
if category.id == category_id:
return category
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Category not found")
# Example usage:
# GET /categories/cat_electronics
# Response for cat_electronics (populated products):
# {
# "id": "cat_electronics",
# "name": "Electronics",
# "description": null,
# "products": [
# { "id": "prod_101", "name": "Wireless Mouse", "price": 25.99, "description": null },
# { "id": "prod_102", "name": "USB-C Hub", "price": 39.99, "description": "7-in-1 adapter." }
# ],
# "manager_email": null
# }
# GET /categories/cat_books
# Response for cat_books (empty products list, populated description):
# {
# "id": "cat_books",
# "name": "Books",
# "description": "A world of stories and knowledge.",
# "products": [],
# "manager_email": null
# }
# GET /categories/cat_furniture
# Response for cat_furniture (null description, empty products list):
# {
# "id": "cat_furniture",
# "name": "Furniture",
# "description": null,
# "products": [],
# "manager_email": null
# }
In this example, the products: List[Product] = Field([], ...) declaration ensures that the products field always defaults to an empty list [] if no products are assigned, rather than None. This means clients can reliably expect an array and iterate over it, even if empty, simplifying their code. The manager_email and description fields, being Optional[str], correctly return null when not set. This dual approach of null for scalar absence and [] for empty collections provides semantic clarity and a consistent contract, which is a hallmark of good API Governance.
Example 3: Conditional Fields and Nested Optional Objects
Sometimes, certain fields or even nested objects are only relevant or present under specific conditions. Managing these can be tricky. We'll look at an example where admin_details is only present for admin users, and an address object might be fully null if not provided.
from typing import Optional, List
from pydantic import BaseModel, Field
from fastapi import FastAPI, HTTPException, status
from enum import Enum
app = FastAPI(
title="Complex User API",
description="Demonstrates conditional fields and nested optional objects.",
version="1.0.0",
)
class UserRole(str, Enum):
USER = "user"
ADMIN = "admin"
MODERATOR = "moderator"
class AdminDetails(BaseModel):
admin_id: str = Field(..., example="adm_001")
permissions: List[str] = Field(..., example=["manage_users", "view_logs"])
last_action_timestamp: str = Field(..., example="2023-10-27T10:00:00Z")
class Address(BaseModel):
street: str = Field(..., example="123 Main St")
city: str = Field(..., example="Anytown")
zip_code: str = Field(..., example="12345")
country: str = Field(..., example="USA")
# Make apartment_number optional
apartment_number: Optional[str] = Field(None, example="Apt 4B")
class ComplexUser(BaseModel):
id: str = Field(..., example="user_777")
username: str = Field(..., example="super_user")
email: str = Field(..., example="super@example.com")
role: UserRole = Field(..., example=UserRole.ADMIN)
# admin_details is Optional, and will be null if user is not an admin
admin_details: Optional[AdminDetails] = Field(None, description="Details only for admin users.")
# address is an Optional nested object. If not provided, it will be null.
address: Optional[Address] = Field(None, description="User's primary address, optional.")
preferences: dict = Field({}, description="User preferences as a key-value store. Can be empty.")
# Mock data
complex_users_db: List[ComplexUser] = [
ComplexUser(
id="user_777",
username="super_user",
email="super@example.com",
role=UserRole.ADMIN,
admin_details=AdminDetails(
admin_id="adm_001",
permissions=["manage_users", "view_logs"],
last_action_timestamp="2023-10-27T10:00:00Z"
),
address=Address(
street="456 Oak Ave",
city="Metropolis",
zip_code="54321",
country="USA",
apartment_number="Unit 10"
),
preferences={"theme": "dark", "notifications": True}
),
ComplexUser(
id="user_888",
username="regular_joe",
email="joe@example.com",
role=UserRole.USER,
# admin_details is not provided, so it will be null
# address is not provided, so it will be null
preferences={} # Empty preferences dictionary
),
ComplexUser(
id="user_999",
username="mod_sue",
email="sue@example.com",
role=UserRole.MODERATOR,
# admin_details is not provided
address=Address(
street="789 Pine Ln",
city="Smallville",
zip_code="98765",
country="Canada"
# apartment_number is not provided, so it will be null within the address object
),
preferences={"locale": "en_CA"}
)
]
@app.get("/complex_users/{user_id}", response_model=ComplexUser, summary="Retrieve a complex user profile")
async def get_complex_user(user_id: str):
"""
Fetches a complex user profile, demonstrating conditional fields
like `admin_details` and optional nested objects like `address`.
"""
for user in complex_users_db:
if user.id == user_id:
return user
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
# Example usage:
# GET /complex_users/user_777 (Admin user with full address and preferences)
# {
# "id": "user_777",
# "username": "super_user",
# "email": "super@example.com",
# "role": "admin",
# "admin_details": {
# "admin_id": "adm_001",
# "permissions": ["manage_users", "view_logs"],
# "last_action_timestamp": "2023-10-27T10:00:00Z"
# },
# "address": {
# "street": "456 Oak Ave",
# "city": "Metropolis",
# "zip_code": "54321",
# "country": "USA",
# "apartment_number": "Unit 10"
# },
# "preferences": {"theme": "dark", "notifications": true}
# }
# GET /complex_users/user_888 (Regular user with no admin_details, no address, empty preferences)
# {
# "id": "user_888",
# "username": "regular_joe",
# "email": "joe@example.com",
# "role": "user",
# "admin_details": null,
# "address": null,
# "preferences": {}
# }
# GET /complex_users/user_999 (Moderator with address, but no apartment_number and no admin_details)
# {
# "id": "user_999",
# "username": "mod_sue",
# "email": "sue@example.com",
# "role": "moderator",
# "admin_details": null,
# "address": {
# "street": "789 Pine Ln",
# "city": "Smallville",
# "zip_code": "98765",
# "country": "Canada",
# "apartment_number": null
# },
# "preferences": {"locale": "en_CA"}
# }
Here, admin_details: Optional[AdminDetails] means that if the user is not an admin, admin_details will be None and thus serialized as null. The address: Optional[Address] field behaves similarly for the entire nested object. Within the Address model, apartment_number: Optional[str] shows how a scalar field within a nested object can also be null. The preferences: dict = Field({}) demonstrates that for generic dictionaries, an empty object {} is the correct representation.
These examples highlight FastAPI's flexibility in managing various forms of "empty" data. The power lies in making conscious decisions about when to use Optional[Type], when to provide default empty collections, and when to potentially omit fields, always with an eye towards client predictability and adherence to a clear api contract as defined by API Governance principles and documented in OpenAPI.
A Comparison Table: When to Use Which "Empty" State
To summarize these best practices, the following table provides a quick reference for common scenarios and the recommended api response patterns. This table itself serves as a crucial component of API Governance, offering a standardized guide for developers.
| Scenario | Recommended Output | Pydantic Type Hint/Default | Rationale | OpenAPI Implication |
|---|---|---|---|---|
Missing single scalar value (e.g., bio, profile_picture_url) for an optional field |
null |
Optional[str] = None or str | None |
Explicitly states absence, field is expected in the schema. Simplifies client parsing as the field is always present. Clear semantic meaning: "no value." | Field has nullable: true. |
Empty list/collection (e.g., tags, products, phone_numbers) |
[] (empty array) |
List[str] = Field([], ...) or List[Product] = [] |
Conveys "no items" in the collection. Easier for clients to iterate over without null checks. Consistent type (always an array). Reduces potential TypeErrors on client side. |
Field type is array. No nullable: true (unless the whole array can be null, which is rare). |
Empty nested object (e.g., address if completely absent) |
null |
Optional[AddressModel] = None |
Signifies the entire sub-object is absent or not provided. Clearer than {} which implies the structure exists but is empty. |
Field has nullable: true and its type is the object schema. |
Empty generic dictionary (e.g., preferences, settings) |
{} (empty object) |
dict = Field({}, ...) |
Explicitly states the key-value store is present but contains no entries. Allows clients to directly access properties without null checks. |
Field type is object. |
Conditionally present field (e.g., admin_token only for admins) |
Omit field (if null) |
Optional[str] = None with response_model_exclude_none=True or exclude_none=True in .model_dump() |
Reduces payload size, signals field is irrelevant if not applicable. Can be interpreted as "field does not exist under these conditions." Requires client to check for field presence. | Field may or may not appear. OpenAPI needs to clarify conditional presence with descriptions. |
Resource not found (e.g., GET /users/999) |
404 Not Found HTTP Status |
N/A (FastAPI HTTPException) |
Use HTTP semantics for errors. null in a 200 response should never signify "resource not found"; it signifies "resource found, but this specific field has no value." |
Error response schema defined for 4xx status codes. |
| Scalar value not applicable/unknown but field is always expected (e.g., a data entry that might not exist yet) | "" (empty string for text), 0 (for numbers), false (for booleans) |
str = Field("", ...), int = Field(0, ...), bool = Field(False, ...) |
When the field is always expected to have a value, and its "empty" state is a valid, distinct value (e.g., an empty comment string, a zero count, a default false flag). Different from null which means "no value at all." |
Field type is string, integer, boolean. No nullable: true. |
This table serves as a robust guideline, embodying a strong API Governance approach to data representation. By consistently applying these rules, developers can build APIs that are predictable, resilient, and easy to consume across diverse client environments.
The Role of API Governance in Null Handling
The meticulous decisions surrounding null values, empty collections, and field omissions, while seemingly granular, aggregate into the overall quality and usability of an API. This is precisely where API Governance plays an indispensable role. API Governance is not merely about setting policies; it's about establishing a framework that ensures the consistent design, development, deployment, and management of APIs across an organization. When it comes to null handling, robust API Governance practices are critical for several reasons:
- Ensuring Consistency Across Teams and Services: In large organizations with multiple development teams building numerous microservices, individual teams might adopt different conventions for
nullhandling if left unchecked. One team might returnnullfor an empty list, while another returns[]. A third might omit optional fields, and a fourth explicitly includes them asnull. This fragmentation quickly leads to a chaoticapiecosystem where client developers face a steep learning curve for each newapithey integrate, negating the benefits of a modular architecture.API Governanceprovides the centralized guidance and tooling to enforce a consistent approach, ensuring all APIs adhere to a unified standard for data representation. - Enhancing Client Predictability and Developer Productivity: A well-governed
apiis predictable. When client developers know exactly how anapiwill behave in edge cases (like absent data), they can write more robust and less error-prone code. This predictability reduces the time spent on debugging and integrating, significantly boosting developer productivity. If anapiconsistently returns[]for empty lists, client code can simply loop through the array without needing to check if the array itself isnull. Ifnullalways means "no value for this optional scalar field," clients can reliably expect the field's presence. - Facilitating Long-term Maintainability and Scalability: APIs have a lifecycle, and they evolve. Without clear
API Governancerules onnullhandling, changes can inadvertently introduce breaking modifications for existing clients. For instance, if anapiinitially omitted a field when its value wasnulland later starts including it asnull, clients expecting omission might break.API Governancemandates versioning strategies and clear communication around such changes, ensuring backward compatibility where possible and providing smooth migration paths when breaking changes are unavoidable. This foresight is crucial for the long-term maintainability and scalability of theapilandscape. - Strengthening API Contracts and
OpenAPICompliance:API Governanceensures thatOpenAPIspecifications are not just generated but accurately reflect the API's behavior, includingnullhandling. It establishes processes for reviewingOpenAPIdefinitions, providing examples, and usingnullable: trueappropriately. This makes theOpenAPIdocument a reliable single source of truth, fostering trust betweenapiproviders and consumers. It's not enough to simply have anOpenAPIdocument; it must be an accurate and comprehensive representation of theapi's contract. - Centralized Management and Observability: Effective
API Governanceoften involves a centralized platform or gateway to manage, monitor, and enforceapipolicies. These platforms provide visibility intoapiusage, performance, and adherence to design standards. For example, if a policy dictates that certainnullvalues should always be omitted to reduce payload size, a gateway could enforce this at a runtime level, or monitoring tools could flagapiresponses that violate the policy.
Effective API Governance is paramount for maintaining consistency, especially when dealing with nuances like null values. Platforms that offer comprehensive api lifecycle management can streamline this process significantly. For instance, APIPark provides robust features that aid in standardizing API formats, managing the entire lifecycle, and ensuring that design principles, including how null is handled, are consistently applied across an organization's api ecosystem. This kind of unified platform helps avoid the pitfalls of ad-hoc design decisions and fosters a more predictable and reliable api landscape, aligning perfectly with the principles of a well-defined api strategy and strong API Governance practices.
Specifically, APIPark's capabilities such as "Unified API Format for AI Invocation" (a principle that can be extended to general REST APIs), "End-to-End API Lifecycle Management," and "API Service Sharing within Teams" directly contribute to establishing and enforcing consistent api design standards. By providing a centralized developer portal and gateway, APIPark empowers organizations to define and publish their api contracts, including detailed specifications on data types and nullability, ensuring that every api adheres to the established API Governance guidelines. This integrated approach, leveraging OpenAPI as the declarative contract, makes the management of api consistency not just a theoretical ideal but a practical reality, significantly reducing the operational overhead associated with disparate api design choices. Its ability to manage traffic forwarding, load balancing, and versioning of published APIs, along with API Resource Access Requirements via approval, further reinforces controlled and governed access to these standardized APIs, preventing ad-hoc deployments that might disregard established null handling conventions.
Leveraging OpenAPI for Null Definitions
The OpenAPI Specification (formerly Swagger Specification) is a language-agnostic, human-readable, and machine-readable interface description for REST APIs. For FastAPI, OpenAPI is not merely an afterthought; it is intrinsically woven into the framework's core, as FastAPI automatically generates a comprehensive OpenAPI schema from your code. This generated schema becomes the definitive contract for your api, and its accurate representation of null handling is crucial for API Governance and consumer trust.
- FastAPI's Automatic
OpenAPIGeneration: When you define Pydantic models withOptional[Type](orType | None), FastAPI intelligently translates these into theOpenAPIschema by including"nullable": truefor the corresponding field. This is a powerful feature as it automatically documents your intent for optional fields. For example,bio: Optional[str]will result in anOpenAPIschema fragment like{"type": "string", "nullable": true}. For lists liketags: List[str], the schema will correctly show{"type": "array", "items": {"type": "string"}}. This automatic generation greatly reduces the manual effort of writing and maintainingOpenAPIdefinitions, allowing developers to focus on implementation while ensuring documentation remains up-to-date. - The Importance of
nullable: trueinOpenAPI3.0: Thenullablekeyword (introduced inOpenAPI3.0, replacingx-nullablefrom 2.0) is a direct, unambiguous way to communicate that a field can legally hold anullvalue. Without this, client generators or developers might assume the field is always present with a non-null value of its specified type, leading to potential parsing errors or incorrect assumptions about data completeness. By explicitly markingnullable: true, you are clearly stating: "this field may contain a string, or it may containnull." - Enhancing the Spec with
examples: Whilenullable: truedefines the possibility ofnull, providingexamplesin yourOpenAPIschema helps developers understand the common scenarios. FastAPI allows you to add examples directly in PydanticFielddefinitions. You can provide examples that demonstrate:- A field with a valid, non-null value.
- A field explicitly set to
null. - An empty array
[]for collection types. - This dual approach of explicit type definition (
nullable: true) and illustrative examples (exampleproperty) makes theOpenAPIdocumentation incredibly rich and helpful.
OpenAPIas the Single Source of Truth: For robustAPI Governance, theOpenAPIspecification should be treated as the canonical definition of yourapi's contract. Any behavior related tonullvalues or empty states must be accurately reflected in this document. This means that both theapiproducer and consumer should refer to theOpenAPIspec to understand expectations. This standardizes the communication and reduces guesswork. Tools likeAPIParkcan ingest theseOpenAPIspecifications to provide a centralized developer portal, offering a unified view of allapicapabilities and their precise contracts, reinforcing the single source of truth principle.- Validation and Compliance Tools: The machine-readable nature of
OpenAPIallows for the development of powerful tooling.- Client SDK Generators: Many tools can automatically generate client SDKs in various programming languages directly from an
OpenAPIspec. These generators leveragenullable: trueto correctly model optional types in the generated code (e.g.,Optional<String>in Java,String?in Swift,Nullable<string>in C#). - API Gateways and Validators:
APIgateways (likeAPIParkitself can act as an AI Gateway, extending to REST API Management, and its features for "End-to-End API Lifecycle Management" naturally encompass specification validation) can use theOpenAPIschema to validate incoming requests and outgoing responses. This ensures that the actual data flowing through theapiadheres to the defined contract, catching inconsistencies like anullvalue for a non-nullable field or an array where an object was expected. - Automated Testing: Test suites can be built to validate
apiresponses against theOpenAPIschema, automatically verifyingnullhandling, data types, and required fields. This provides continuous assurance that theapiremains compliant with its documented contract.
- Client SDK Generators: Many tools can automatically generate client SDKs in various programming languages directly from an
By conscientiously leveraging OpenAPI in FastAPI, especially concerning null definitions and examples, developers can build an api that is not only high-performing but also impeccably documented and governed. This transparent and standardized approach simplifies integration, reduces errors, and significantly enhances the overall developer experience, which is the ultimate goal of effective API Governance.
Conclusion
The seemingly minor decision of how an api returns null or handles empty data structures can have profound implications for its usability, consistency, and long-term maintainability. In the realm of modern api development with frameworks like FastAPI, where clarity and speed are paramount, deliberate design choices regarding these "empty" states are not merely good practice—they are essential for building robust and developer-friendly systems. This extensive exploration has traversed the nuances of null, distinguished it from other forms of emptiness, and demonstrated FastAPI's sophisticated capabilities, powered by Pydantic and Python's type hinting, for managing these distinctions.
We've established that the bedrock of effective null handling lies in adherence to core design principles: * Consistency is non-negotiable, ensuring a uniform approach across your entire api landscape. * Semantic Clarity dictates that each representation (null, [], {}, omission) precisely conveys its intended meaning. * Consumer-Centric Design prioritizes the ease with which client developers can parse and interact with your api. * Explicit Documentation through OpenAPI serves as the indispensable contract, unambiguously detailing expected behaviors. * Clear Separation of Concerns between data absence and error conditions prevents ambiguity and misuse of HTTP status codes. * Careful Versioning acknowledges that changes to null behavior are breaking changes, requiring thoughtful management.
These principles, when diligently applied, elevate an api from a mere functional interface to a reliable and intuitive platform. They form the very essence of strong API Governance, preventing the proliferation of inconsistent patterns and fostering a cohesive api ecosystem. Tools and platforms, such as APIPark, play a pivotal role in this governance, offering comprehensive solutions for api lifecycle management, standardization, and centralized oversight, thereby ensuring that these best practices are not just aspirational but consistently enforced across an organization. By integrating features that streamline api design, facilitate sharing, and enforce access policies, platforms like APIPark empower teams to maintain high standards of api quality, including the often-overlooked details of null handling, all while leveraging the power of OpenAPI as the declarative contract.
In conclusion, mastering the art of returning null in FastAPI is fundamentally about thoughtful API design and rigorous API Governance. It's about recognizing that every piece of data, or its absence, sends a message to the consuming application. By making these messages clear, consistent, and well-documented through OpenAPI specifications, you empower developers, reduce integration complexities, and ultimately build an api that is not only performant and scalable but also a true joy to work with.
Frequently Asked Questions (FAQ)
1. What is the fundamental difference between null and an empty array ([]) in API responses? The fundamental difference lies in their semantic meaning and type. null explicitly signifies the absence of a value for a particular field, implying that the field exists in the schema but currently holds nothing. For example, bio: null means there's no biography. An empty array [], however, represents a collection that contains zero items. It explicitly states that a list or array is present and valid, but it is currently devoid of elements. For example, tags: [] means the product has no tags, but tags is still an array type. Using [] for empty collections is generally preferred as it simplifies client-side parsing by always providing an iterable object.
2. How does FastAPI handle null values by default for Optional fields defined with Pydantic? By default, when you define a field as Optional[Type] (or Type | None) in a Pydantic model in FastAPI, if that field's value is not provided during model instantiation or is explicitly set to None, FastAPI will serialize it as null in the JSON response. Furthermore, FastAPI's automatic OpenAPI generation will mark this field with "nullable": true" in the OpenAPI schema, explicitly indicating to API consumers that the field can legally contain a null value.
3. When should I consider omitting null fields from my API responses instead of returning explicit null? You might consider omitting null fields (e.g., using response_model_exclude_none=True in FastAPI path operations or exclude_none=True when dumping a Pydantic model) in scenarios where: * Payload size optimization is critical: For APIs with many optional fields that are frequently null, omitting them can reduce the response payload size. * Sparse data: For resources where many fields are often absent, omitting null can make the response "cleaner" and highlight only the truly relevant data. However, this comes with a trade-off: clients must then check for the presence of a field's key, not just its value, which can complicate parsing logic and make the API contract less explicit. It is crucial to document this behavior clearly in your OpenAPI specification if you choose this approach.
4. What role does API Governance play in consistent null handling? API Governance is crucial for ensuring consistency and predictability in null handling across an organization's entire api landscape. It involves establishing clear, organization-wide standards for how null values, empty collections, and other "empty" states are represented. This prevents individual teams from adopting disparate conventions, which would lead to fragmented and confusing apis. Strong API Governance ensures that these standards are documented (often via OpenAPI), enforced (potentially through tools like API gateways or linting), and communicated effectively to all api producers and consumers, thereby enhancing client predictability, developer productivity, and long-term maintainability.
5. How does OpenAPI help with null definitions, and why is it important? OpenAPI helps with null definitions by providing a standardized, machine-readable way to declare that a field can legitimately be null using the "nullable": true property. This is vital because it explicitly communicates the api's contract to consumers and automated tools. Without it, client SDK generators or developers might incorrectly assume a field will always have a non-null value, leading to runtime errors or incorrect client-side logic. By accurately reflecting null capabilities in the OpenAPI specification, the documentation becomes a reliable single source of truth, facilitating easier integration, automated testing, and better API Governance.
🚀You can securely and efficiently call the OpenAI API on APIPark in just two steps:
Step 1: Deploy the APIPark AI gateway in 5 minutes.
APIPark is developed based on Golang, offering strong product performance and low development and maintenance costs. You can deploy APIPark with a single command line.
curl -sSO https://download.apipark.com/install/quick-start.sh; bash quick-start.sh

In my experience, you can see the successful deployment interface within 5 to 10 minutes. Then, you can log in to APIPark using your account.

Step 2: Call the OpenAI API.

