---
last_review: "2026-05-06"
last_reviewer: "s.kaviani"
documented_code: [ ]
---
```{tags} user, tutorial
```
% Issue: https://gitlab.indiscale.com/caosdb/src/linkahead-docs/-/issues/72
# First Steps with PyLinkAhead
In this tutorial, you will connect to a LinkAhead server using the Python client, run queries, work
with the returned data, and extract property values for analysis. The web interface is convenient
for interactive browsing, while PyLinkAhead is designed for scripting, automation, and data analysis
workflows.
| Concept | PyLinkAhead |
|-------------------------|-----------------------------|
| Connect to a server | `db.configure_connection()` |
| Run a query | `db.execute_query()` |
| Access a record | `Container[0]`, `Record` |
| Read a property | `.get_property()` |
| Read a property's value | `.value` |
| Record id | `.id` |
| Single-result queries | `unique=True` |
| Download a file | `.download()` |
## Prerequisites
Before starting, make sure that:
- PyLinkAhead is installed: see the [setup guide](/how_to/dev_guides/pylib/README_SETUP_pylib.md).
- A connection is configured for your instance:
see [How to Configure PyLinkAhead](/how_to/dev_guides/pylib/configure_pylinkahead.md). For this
tutorial you can skip this and use the public demo instead.
- You have access to a LinkAhead instance: you can use
the [public demo](https://demo.indiscale.com).
- You are familiar with basic LinkAhead concepts such as Records, RecordTypes, and Properties: see
the [LinkAhead Web Interface Tutorial](/tutorial/webinterface/first_steps.md).
- You know the basics of the LinkAhead query language: see
the [LinkAhead Query Tutorial](/tutorial/webinterface/query_find.md).
% TODO: The specific values in the examples below (number of results, property ids, record ids)
% TODO: depend on the current state of the demo instance and should be verified if the demo data
% TODO: changes.
The examples below use the public demo instance hosted by [IndiScale](https://indiscale.com).
## Setup
### Connecting to a LinkAhead Server
In regular use, PyLinkAhead reads connection settings from `~/.pylinkahead.ini` automatically when
you import the library, no extra code needed. This tutorial uses `configure_connection()` instead to
keep the examples self-contained and work with the public demo out of the box.
Open a Python interactive session (`python3` in your terminal) or create a new `.py` script file.
All code examples on this page are Python, not shell commands.
First, import the Python client:
```python
import linkahead as db
```
Then configure the connection to the demo instance:
```{literalinclude} /.assets/tests/tutorials/pylib/test_first_steps.py
:start-after: "# ---- LINK-1 ----"
:end-before: "# ---- LINK-2 ----"
:language: python
:dedent:
```
:::{note}
`password_method="plain"` is suitable only for the public demo instance and should not be used for
production systems.
:::
## Querying Data
### Running Your First Query
Queries in PyLinkAhead use the same query language as the web interface. The demo instance contains
a small music dataset with guitar records and musical analyses.
```{literalinclude} /.assets/tests/tutorials/pylib/test_first_steps.py
:start-after: "# ---- LINK-3 ----"
:end-before: "# ---- LINK-4-1 ----"
:language: python
:dedent:
```
The result is returned as a `Container` object:
```{literalinclude} /.assets/tests/tutorials/pylib/test_first_steps.py
:start-after: "EXPECTED_OUTPUT_1 "
:end-before: "# END_EXPECTED_OUTPUT_1"
:caption: Output:
:class: text-output
:language: text
:dedent:
```
A `Container` behaves similarly to a Python list and contains the matching LinkAhead entities. You
can check how many results were returned:
```{literalinclude} /.assets/tests/tutorials/pylib/test_first_steps.py
:start-after: "# ---- LINK-4-2 ----"
:end-before: "# ---- LINK-5 ----"
:language: python
:dedent:
```
```{literalinclude} /.assets/tests/tutorials/pylib/test_first_steps.py
:start-after: "EXPECTED_OUTPUT_2 "
:end-before: "# END_EXPECTED_OUTPUT_2"
:caption: Output:
:class: text-output
:language: text
:dedent:
```
### Working with Records
You can access individual records using normal list indexing and print them to see their full
representation including parent and properties:
```{literalinclude} /.assets/tests/tutorials/pylib/test_first_steps.py
:start-after: "# ---- LINK-6 ----"
:end-before: "# ---- LINK-7 ----"
:language: python
:dedent:
```
```{code-block} text
:caption: Output:
:class: text-output
48.0
False
132
```
The `...` in the output above is an abbreviation; the actual output includes additional attributes
on each property.
A `Record` contains metadata and properties that can be accessed programmatically.
### Accessing Properties
Properties can be accessed by name:
```{literalinclude} /.assets/tests/tutorials/pylib/test_first_steps.py
:start-after: "# ---- LINK-12 ----"
:end-before: "# ---- LINK-13 ----"
:language: python
:dedent:
```
```{code-block} text
:caption: Output:
:class: text-output
48.0
```
To access only the value:
```{literalinclude} /.assets/tests/tutorials/pylib/test_first_steps.py
:start-after: "# ---- LINK-13 ----"
:end-before: "# ---- LINK-14 ----"
:language: python
:dedent:
```
```{code-block} text
:caption: Output:
:class: text-output
48.0
```
Properties can also be accessed by their numeric id:
```{literalinclude} /.assets/tests/tutorials/pylib/test_first_steps.py
:start-after: "# ---- LINK-14 ----"
:end-before: "# ---- LINK-15 ----"
:language: python
:dedent:
```
```{code-block} text
:caption: Output:
:class: text-output
48.0
```
Using ids can be useful when names are not guaranteed to be unique across a data model.
### Querying for a Single Record
Pass `unique=True` when you know a query returns exactly one record. It is useful as a safety check:
instead of silently returning the first item from a list, it raises an error if zero or more than
one result is found, so unexpected data does not go unnoticed. The call also returns a `Record`
directly instead of a `Container`, so you do not need to index into a list. The demo contains one
`MusicalAnalysis` record with `quality_factor=0.08`:
```{literalinclude} /.assets/tests/tutorials/pylib/test_first_steps.py
:start-after: "# ---- LINK-16 ----"
:end-before: "# ---- LINK-17 ----"
:language: python
:dedent:
```
You can then access the record type and id directly:
```{literalinclude} /.assets/tests/tutorials/pylib/test_first_steps.py
:start-after: "# ---- LINK-17 ----"
:end-before: "# ---- LINK-18 ----"
:language: python
:dedent:
```
```{code-block} text
:caption: Output:
:class: text-output
123
```
Record ids are useful for constructing follow-up queries:
```{literalinclude} /.assets/tests/tutorials/pylib/test_first_steps.py
:start-after: "# ---- LINK-19 ----"
:end-before: "# ---- LINK-20 ----"
:language: python
:dedent:
```
```{code-block} text
:caption: Output:
:class: text-output