What high-level problem are you trying to solve?
pyODK is a wrapper for communicating with the ODK Central API. It seems that there is no way to download the attachments of a submission using this library.
The submission object object returned from the Client contains an attachment field but it’s None always. The response from ODK to that query only contains metadata information about the submission.
from pyodk import Client
client = Client()
project_id = 3
form_id = "form"
submission_id = "uuid:f686c5f5-7edc-4798-967f-80cc1e6180d1"
print(
client.submissions.get(
instance_id=submission_id,
form_id=form_id,
project_id=project_id,
)
)
Whose output is this:
instanceId='uuid:f686c5f5-7edc-4798-967f-80cc1e6180d1' submitterId=61 createdAt=datetime.datetime(2026, 2, 3, 15, 50, 18, 682000, tzinfo=TzInfo(UTC)) deviceId=None reviewState=None userAgent='Enketo/7.5.1 Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36' instanceName=None updatedAt=None attachments=None
To download the attachments of a submission from ODK Central API, an HTTP endpoint can be used, and it works. The purpose of pyODK is to abstract those details, so it would be nice to have that feature in it.
Any ideas on how ODK could help you solve it?
Adding a method or function to the Client class with the following signature:
def download_attachment(self, project_id : int, form_id : str, submission_id : str, attachment_name : str) -> bytes
# do the HTTP request to the download attachment endpoint to obtain the bytes.
To solve the problem I've implemented the function like this:
def download_attachment(
client: Client,
project_id: int,
form_id: str,
submission_id: str,
attachment_name: str,
) -> bytes:
user = client.config.central.username
password = client.config.central.password
url = client.config.central.base_url
import requests
import base64
credentials = f"{user}:{password}"
encoded_credentials = base64.b64encode(credentials.encode("utf-8")).decode("utf-8")
headers = {"Authorization": f"Basic {encoded_credentials}"}
query = f"{url}v1/projects/{project_id}/forms/{form_id}/submissions/{submission_id}/attachments/{attachment_name}"
response = requests.get(query, headers=headers)
return response.content
Upload any helpful links, sketches, and videos.
I’m building a website whose goal is showing statistical information about the submissions of a selected form. My website gets the data from an API I’m running in python using FastAPI, which under the hood uses the *pyODK* client to obtain the data.
