Skip to content

Endpoints

Accessed as sepal.files, sepal.tasks, and sepal.recipes. Each sync class below has an identical async twin (AsyncUserFilesEndpoint, AsyncTasksEndpoint, AsyncRecipesEndpoint) whose methods mirror these with await.

Files — sepal.files

pysepal_api.endpoints.user_files.UserFilesEndpoint

UserFilesEndpoint(http: Client)
Source code in src/pysepal_api/endpoints/_base.py
20
21
def __init__(self, http: httpx.Client) -> None:
    self._http = http

list

list(
    folder: str = ".",
    *,
    extensions: Sequence[str] | None = None,
    include_hidden: bool = False
) -> DirectoryListing
Source code in src/pysepal_api/endpoints/user_files.py
90
91
92
93
94
95
96
97
98
def list(
    self,
    folder: str = ".",
    *,
    extensions: Sequence[str] | None = None,
    include_hidden: bool = False,
) -> DirectoryListing:
    resp = self._send(_list_spec(folder, extensions, include_hidden))
    return parse_one(resp, DirectoryListing)

read_bytes

read_bytes(file_path: str) -> bytes

Download a file and return its raw bytes.

Source code in src/pysepal_api/endpoints/user_files.py
100
101
102
def read_bytes(self, file_path: str) -> bytes:
    """Download a file and return its raw bytes."""
    return self._send(_download_spec(file_path)).content

read_text

read_text(file_path: str) -> str

Download a file and decode it as text (charset from the response).

Source code in src/pysepal_api/endpoints/user_files.py
104
105
106
def read_text(self, file_path: str) -> str:
    """Download a file and decode it as text (charset from the response)."""
    return self._send(_download_spec(file_path)).text

read_json

read_json(file_path: str) -> Any

Download a file and parse it as JSON.

Source code in src/pysepal_api/endpoints/user_files.py
108
109
110
def read_json(self, file_path: str) -> Any:
    """Download a file and parse it as JSON."""
    return _parse_json(self._send(_download_spec(file_path)))

write

write(
    file_path: str,
    content: str | bytes,
    *,
    overwrite: bool = False
) -> FileWriteResult

Upload a file via multipart/form-data (form field file).

Raises Conflict if the file already exists and overwrite is false.

Source code in src/pysepal_api/endpoints/user_files.py
112
113
114
115
116
117
118
119
120
121
122
123
124
def write(
    self,
    file_path: str,
    content: str | bytes,
    *,
    overwrite: bool = False,
) -> FileWriteResult:
    """Upload a file via multipart/form-data (form field `file`).

    Raises `Conflict` if the file already exists and `overwrite` is false.
    """
    resp = self._send(_write_spec(file_path, content, overwrite))
    return parse_one(resp, FileWriteResult, default={})

mkdir

mkdir(path: str, *, parents: bool = True) -> PurePosixPath

Create a folder under the user workspace; idempotent on 409/403.

SEPAL returns 403 (not 409) for an already-existing folder in some deployments, so both are swallowed to keep mkdir idempotent. Caveat: that also hides a genuine permission failure — it will surface on the first write into the folder instead.

Source code in src/pysepal_api/endpoints/user_files.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def mkdir(self, path: str, *, parents: bool = True) -> PurePosixPath:
    """Create a folder under the user workspace; idempotent on 409/403.

    SEPAL returns 403 (not 409) for an already-existing folder in some
    deployments, so both are swallowed to keep `mkdir` idempotent. Caveat:
    that also hides a genuine permission failure — it will surface on the
    first write into the folder instead.
    """
    relative = sanitize_write_path(path)
    try:
        self._send(_mkdir_spec(path, parents))
    except (Conflict, Forbidden):
        pass
    return relative

module_dir

module_dir(module_name: str) -> PurePosixPath

Create and return /home/sepal-user/module_results/{module_name}.

Source code in src/pysepal_api/endpoints/user_files.py
141
142
143
144
def module_dir(self, module_name: str) -> PurePosixPath:
    """Create and return `/home/sepal-user/module_results/{module_name}`."""
    self.mkdir(str(module_results_relative(module_name)), parents=True)
    return module_results_path(module_name)

Tasks — sepal.tasks

pysepal_api.endpoints.tasks.TasksEndpoint

TasksEndpoint(http: Client)
Source code in src/pysepal_api/endpoints/_base.py
20
21
def __init__(self, http: httpx.Client) -> None:
    self._http = http

submit

submit(
    operation: str,
    params: dict[str, Any],
    *,
    recipe_id: str | None = None,
    instance_type: str | None = None
) -> Task
Source code in src/pysepal_api/endpoints/tasks.py
80
81
82
83
84
85
86
87
88
89
def submit(
    self,
    operation: str,
    params: dict[str, Any],
    *,
    recipe_id: str | None = None,
    instance_type: str | None = None,
) -> Task:
    resp = self._send(_submit_spec(operation, params, recipe_id, instance_type))
    return parse_one(resp, Task)

list

list(
    status: TaskState | str | None = None,
    *,
    output_path: str | None = None,
    destination: str | None = None
) -> list[Task]
Source code in src/pysepal_api/endpoints/tasks.py
91
92
93
94
95
96
97
98
99
def list(
    self,
    status: TaskState | str | None = None,
    *,
    output_path: str | None = None,
    destination: str | None = None,
) -> list[Task]:
    resp = self._send(_list_spec(status, output_path, destination))
    return parse_many(resp, Task)

get

get(task_id: str, *, details: bool = False) -> Task
Source code in src/pysepal_api/endpoints/tasks.py
101
102
103
def get(self, task_id: str, *, details: bool = False) -> Task:
    resp = self._send(_get_spec(task_id, details))
    return parse_one(resp, Task)

cancel

cancel(task_id: str) -> None
Source code in src/pysepal_api/endpoints/tasks.py
105
106
def cancel(self, task_id: str) -> None:
    self._send(_action_spec(task_id, "cancel"))

remove

remove(task_id: str) -> None
Source code in src/pysepal_api/endpoints/tasks.py
108
109
def remove(self, task_id: str) -> None:
    self._send(_action_spec(task_id, "remove"))

restart

restart(task_id: str) -> None

Maps to SEPAL's execute route.

Source code in src/pysepal_api/endpoints/tasks.py
111
112
113
def restart(self, task_id: str) -> None:
    """Maps to SEPAL's `execute` route."""
    self._send(_action_spec(task_id, "execute"))

wait

wait(
    task_id: str,
    *,
    poll_interval: float = 5.0,
    timeout: float | None = None
) -> Task

Poll get(task_id) until terminal. Raises on FAILED/CANCELED/timeout.

Source code in src/pysepal_api/endpoints/tasks.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
def wait(
    self, task_id: str, *, poll_interval: float = 5.0, timeout: float | None = None
) -> Task:
    """Poll `get(task_id)` until terminal. Raises on FAILED/CANCELED/timeout."""
    start = time.monotonic()
    while True:
        task = self.get(task_id)
        done = _wait_step(task, task_id)
        if done is not None:
            return done
        if timeout is not None and time.monotonic() - start >= timeout:
            raise TaskTimeout(
                f"Task {task_id} did not reach terminal state in {timeout}s", task=task
            )
        time.sleep(poll_interval)

Recipes — sepal.recipes

pysepal_api.endpoints.recipes.RecipesEndpoint

RecipesEndpoint(http: Client)
Source code in src/pysepal_api/endpoints/_base.py
20
21
def __init__(self, http: httpx.Client) -> None:
    self._http = http

list

list() -> RecipeSummaries
Source code in src/pysepal_api/endpoints/recipes.py
55
56
def list(self) -> RecipeSummaries:
    return _summaries(self._send(RequestSpec("GET", "/api/processing-recipes")))

get

get(recipe_id: str) -> Any

Load a recipe as parsed JSON. Use get_raw for the exact bytes.

Source code in src/pysepal_api/endpoints/recipes.py
58
59
60
def get(self, recipe_id: str) -> Any:
    """Load a recipe as parsed JSON. Use `get_raw` for the exact bytes."""
    return _parse_json(self._send(RequestSpec("GET", f"/api/processing-recipes/{recipe_id}")))

get_raw

get_raw(recipe_id: str) -> bytes

Load a recipe body exactly as stored, without JSON parsing.

Source code in src/pysepal_api/endpoints/recipes.py
62
63
64
def get_raw(self, recipe_id: str) -> bytes:
    """Load a recipe body exactly as stored, without JSON parsing."""
    return self._send(RequestSpec("GET", f"/api/processing-recipes/{recipe_id}")).content

save

save(
    recipe_id: str,
    *,
    project_id: str,
    type: str,
    name: str,
    contents: RecipeContents
) -> RecipeSummaries

POST a recipe. SEPAL stores the gzipped body as-is; pass either already-encoded bytes/str or a JSON-serializable structure that the client will encode for you.

Source code in src/pysepal_api/endpoints/recipes.py
66
67
68
69
70
71
72
73
74
75
76
77
78
def save(
    self,
    recipe_id: str,
    *,
    project_id: str,
    type: str,
    name: str,
    contents: RecipeContents,
) -> RecipeSummaries:
    """POST a recipe. SEPAL stores the gzipped body as-is; pass either
    already-encoded bytes/str or a JSON-serializable structure that the
    client will encode for you."""
    return _summaries(self._send(_save_spec(recipe_id, project_id, type, name, contents)))

delete

delete(recipe_id: str) -> RecipeSummaries
Source code in src/pysepal_api/endpoints/recipes.py
80
81
def delete(self, recipe_id: str) -> RecipeSummaries:
    return _summaries(self._send(RequestSpec("DELETE", f"/api/processing-recipes/{recipe_id}")))