Documentation Index
Fetch the complete documentation index at: https://mintlify.com/modal-labs/modal-client/llms.txt
Use this file to discover all available pages before exploring further.
This guide covers the fundamental patterns for using Modal’s Python SDK.
Creating an app
Every Modal project starts with an App object:
import modal
app = modal.App("my-first-app")
The app name is optional but recommended for identifying your deployments.
Writing functions
Decorate any Python function with @app.function() to run it on Modal:
@app.function()
def hello(name: str):
return f"Hello, {name}!"
Running locally
To run a Modal app locally, use the modal run command:
This executes your local Python script and runs decorated functions in Modal containers.
Calling functions
There are several ways to invoke Modal functions:
Remote execution
Call .remote() to execute the function in a Modal container:
if __name__ == "__main__":
result = hello.remote("Alice")
print(result) # "Hello, Alice!"
Local execution
Call .local() to run the function locally:
result = hello.local("Bob")
Process multiple inputs in parallel using .map():
names = ["Alice", "Bob", "Charlie"]
results = list(hello.map(names))
# ["Hello, Alice!", "Hello, Bob!", "Hello, Charlie!"]
Using custom images
Specify the container environment with the image parameter:
image = modal.Image.debian_slim().pip_install(
"pandas",
"numpy",
"scikit-learn"
)
@app.function(image=image)
def analyze_data(data):
import pandas as pd
import numpy as np
# Your analysis code here
return results
Working with context
Modal provides utilities to check execution context:
from modal import is_local
@app.function()
def my_function():
if is_local():
print("Running locally")
else:
print("Running in Modal container")
Get information about the current function execution:
from modal import current_input_id, current_function_call_id
@app.function()
def process_task():
input_id = current_input_id()
call_id = current_function_call_id()
print(f"Processing input {input_id} from call {call_id}")
Using secrets
Securely pass credentials to your functions:
@app.function(
secrets=[modal.Secret.from_name("my-api-key")]
)
def call_api():
import os
api_key = os.environ["API_KEY"]
# Use the API key
Mounting volumes
Persist data across function invocations:
volume = modal.Volume.from_name("my-data", create_if_missing=True)
@app.function(
volumes={'/data': volume}
)
def save_results(data):
with open('/data/results.json', 'w') as f:
json.dump(data, f)
volume.commit() # Persist changes
GPU functions
Request GPU resources for compute-intensive tasks:
@app.function(
gpu="T4", # or "A10G", "A100", etc.
image=modal.Image.debian_slim().pip_install("torch")
)
def train_model():
import torch
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Training code
Retry policies
Configure automatic retries for transient failures:
@app.function(
retries=modal.Retries(
max_retries=3,
backoff_coefficient=2.0,
initial_delay=1.0
)
)
def flaky_function():
# Function that might fail
pass
Timeouts
Set execution time limits:
@app.function(timeout=300) # 5 minutes
def long_running_task():
# Task code
pass
Local entrypoints
Define code that runs locally when the script is executed:
@app.local_entrypoint()
def main():
# This runs locally
result = hello.remote("World")
print(f"Result: {result}")
Then run with:
Complete example
Here’s a complete example combining multiple concepts:
import modal
app = modal.App("data-processor")
image = modal.Image.debian_slim().pip_install("pandas", "requests")
volume = modal.Volume.from_name("processed-data", create_if_missing=True)
@app.function(
image=image,
volumes={'/data': volume},
secrets=[modal.Secret.from_name("api-credentials")],
retries=3
)
def process_record(record_id: int):
import pandas as pd
import requests
import os
# Fetch data from API
api_key = os.environ["API_KEY"]
response = requests.get(
f"https://api.example.com/records/{record_id}",
headers={"Authorization": f"Bearer {api_key}"}
)
data = response.json()
# Process with pandas
df = pd.DataFrame([data])
df['processed'] = True
# Save to volume
df.to_csv(f'/data/record_{record_id}.csv', index=False)
volume.commit()
return record_id
@app.local_entrypoint()
def main():
# Process records in parallel
record_ids = range(1, 101)
results = list(process_record.map(record_ids))
print(f"Processed {len(results)} records")
Next steps
Async support
Learn how to use async/await patterns
Web endpoints
Create HTTP endpoints for your functions
Scheduled jobs
Run functions on schedules
CLI reference
Explore all CLI commands