The Metaflow Client API allows you to access and analyze runs, steps, tasks, and data artifacts from any Metaflow flow. Use it for analysis, debugging, or building tools on top of Metaflow.
Getting Started
Import the main classes:
from metaflow import Flow, Run, Step, Task, DataArtifact, Metaflow
The entry point for accessing all flows:
from metaflow import Metaflow
# List all flows
for flow in Metaflow():
print(flow.id)
Flow Objects
Access a specific flow by name:
from metaflow import Flow
# Get a flow
flow = Flow('MyFlow')
# Get the latest run
latest_run = flow.latest_run
# Get a specific run
run = flow['RunID']
# Iterate over all runs
for run in flow:
print(f"Run: {run.id}, Created: {run.created_at}")
Flow Attributes
flow = Flow('MyFlow')
print(flow.id) # Flow name
print(flow.latest_run) # Most recent run
print(flow.latest_successful_run) # Most recent successful run
Run Objects
A Run represents a single execution of a flow:
from metaflow import Run
# Get a run directly
run = Run('MyFlow/123')
# Or from a flow
run = Flow('MyFlow').latest_run
# Access run properties
print(run.id) # Run number
print(run.pathspec) # 'MyFlow/123'
print(run.created_at) # Datetime
print(run.finished_at) # Datetime (if completed)
print(run.successful) # Boolean
print(run.finished) # Boolean
print(run.tags) # Set of all tags
print(run.user_tags) # Set of user tags
print(run.system_tags) # Set of system tags
Accessing Steps
# Get a specific step
step = run['start']
# Iterate over all steps
for step in run:
print(f"Step: {step.id}")
# Get step by name
if 'train' in run:
train_step = run['train']
Run Data
Access data artifacts from any step in the run:
run = Flow('MyFlow').latest_run
# Access data from a specific step
data = run['end'].task.data
print(data.accuracy)
print(data.model)
Step Objects
A Step contains all tasks for that step:
# Get a step
step = Step('MyFlow/123/train')
# Or from a run
step = run['train']
# Access step properties
print(step.id) # Step name
print(step.pathspec) # 'MyFlow/123/train'
print(step.created_at) # Datetime
print(step.finished_at) # Datetime (if completed)
# Get the task (or first task if multiple)
task = step.task
# Iterate over all tasks (in foreach steps)
for task in step:
print(f"Task: {task.id}, Index: {task.index}")
Task Objects
A Task represents a single execution of a step:
# Get a task
task = Task('MyFlow/123/train/456')
# Or from a step
task = step.task
# Access task properties
print(task.id) # Task ID
print(task.pathspec) # 'MyFlow/123/train/456'
print(task.index) # Index in foreach (or None)
print(task.created_at) # Datetime
print(task.finished_at) # Datetime
print(task.successful) # Boolean
print(task.finished) # Boolean
print(task.exception) # Exception object (if failed)
print(task.tags) # Set of tags
Task Data
Access all artifacts from a task:
task = run['train'].task
# Access data container
data = task.data
# Access specific artifacts
print(data.model) # The model artifact
print(data.accuracy) # The accuracy artifact
print(data.loss) # The loss artifact
# Check if artifact exists
if 'model' in task.data:
model = data.model
# Iterate over artifact names
for artifact_name in dir(data):
if not artifact_name.startswith('_'):
print(artifact_name)
# Get all metadata
for m in task.metadata:
print(f"{m.name}: {m.value}")
# Get metadata as dict (latest values)
metadata = task.metadata_dict
print(metadata['attempt'])
print(metadata['runtime'])
DataArtifact Objects
Access individual artifacts:
# Get a specific artifact
artifact = DataArtifact('MyFlow/123/train/456/model')
# Or from a task
artifact = task['model']
# Access artifact properties
print(artifact.id) # Artifact name ('model')
print(artifact.data) # The actual Python object
print(artifact.sha) # Unique content hash
print(artifact.size) # Size in bytes
print(artifact.created_at) # Datetime
Common Patterns
Get Latest Successful Run
flow = Flow('MyFlow')
latest = flow.latest_successful_run
if latest:
print(f"Latest run: {latest.id}")
print(f"Accuracy: {latest['end'].task.data.accuracy}")
else:
print("No successful runs found")
Compare Runs
flow = Flow('MyFlow')
# Get last 10 runs
runs = list(flow)[:10]
# Compare accuracies
for run in runs:
if run.successful:
accuracy = run['end'].task.data.accuracy
print(f"Run {run.id}: {accuracy:.3f}")
Analyze Foreach Results
run = Flow('MyFlow').latest_run
step = run['train'] # Foreach step
# Collect results from all tasks
results = []
for task in step:
results.append({
'index': task.index,
'accuracy': task.data.accuracy,
'params': task.data.params
})
# Find best result
best = max(results, key=lambda x: x['accuracy'])
print(f"Best params: {best['params']}")
print(f"Best accuracy: {best['accuracy']}")
Access Parent/Child Tasks
task = Task('MyFlow/123/join/456')
# Get parent tasks
for parent in task.parent_tasks:
print(f"Parent: {parent.pathspec}")
print(f"Result: {parent.data.result}")
# Get child tasks
for child in task.child_tasks:
print(f"Child: {child.pathspec}")
Download Code Package
run = Flow('MyFlow').latest_run
# Access code used for this run
if run.code:
print(f"Script: {run.code.script_name}")
print(f"Location: {run.code.path}")
# Get source code
print(run.code.flowspec)
# Extract to directory
with run.code.extract() as tmp_dir:
print(f"Code extracted to: {tmp_dir}")
# Use files in tmp_dir...
# Get runs with specific tags
for run in Flow('MyFlow'):
if 'production' in run.tags:
print(f"Production run: {run.id}")
# Filter in iteration
production_runs = [
run for run in Flow('MyFlow')
if 'production' in run.tags
]
Namespace Management
Control which runs are visible:
from metaflow import namespace, get_namespace, default_namespace
# See current namespace
print(get_namespace()) # Returns current user
# Switch namespace to see another user's runs
namespace('other_user')
# See all runs regardless of namespace
namespace(None)
# Reset to default
default_namespace()
Switch between metadata providers:
from metaflow import metadata, get_metadata, default_metadata
# See current provider
print(get_metadata())
# Switch to local metadata
metadata('local')
# Switch to service metadata
metadata('https://metadata-service.example.com')
# Reset to default
default_metadata()
Best Practices
Cache flow objects: If analyzing multiple runs, get the Flow object once and reuse it:flow = Flow('MyFlow')
for run in flow:
# Analyze run...
Use latest_successful_run: For production analysis, use latest_successful_run instead of latest_run to skip failed runs.
Check existence: Always check if artifacts or runs exist before accessing:if 'model' in task.data:
model = task.data.model
Large artifacts: Loading large artifacts via task.data downloads them locally. For large datasets, consider using external storage (S3, GCS) and storing only references.
Object Hierarchy
The Client API follows this hierarchy:
Metaflow (all flows)
└─ Flow (specific flow)
└─ Run (execution of flow)
└─ Step (step in run)
└─ Task (execution of step)
└─ DataArtifact (data from task)
Each level can be accessed directly by pathspec:
Flow('MyFlow')
Run('MyFlow/123')
Step('MyFlow/123/train')
Task('MyFlow/123/train/456')
DataArtifact('MyFlow/123/train/456/model')