Skip to main content

PQL

PQL (short for Porla Query Language) is a small query language built into Porla for finding torrents. The same syntax works in the web UI search field, the torrents.list query filter and in plugins through PoQuery.

In true Porla fashion, it maps mostly 1:1 with the libtorrent API for torrent_status which means some clunky names here and there, but makes it easier to maintain.

Example queries​

Quick lookups​

QueryDescription
errc:trueTorrents with an error, such as a missing file, a full disk or a failed move.
ubuntu isoFree text search; both words must appear in the name. A bare word that looks like an info hash also matches info hash prefixes.
name:"ubuntu 24.04" OR info_hash:3f9aac15Finds a release by its exact name (quotes keep the space) or by the start of its info hash.
eta:<10mDownloads expected to finish in the next 10 minutes.
upload_payload_rate:>1mb/sTorrents currently uploading faster than 1 MB/s.
completed_time:2026-10-01..2026-10-05Torrents that finished between 1st and 5th Oct, both days included.

Organizing​

QueryDescription
-has:$userdata.categoryTorrents with no category set.
$userdata.category:=linux added_time:-7dTorrents in the linux category that were added in the last week.
save_path:/mnt/old total:>50gbLarge torrents still in an old save path.

Troubleshooting​

QueryDescription
has_metadata:false added_time:<=30mMagnet links that are still fetching metadata after 30 minutes, which usually means nobody is sharing them.
announcing_to_trackers:true flags:~paused -has:current_tracker added_time:<-1hTorrents that should be using trackers but have had no tracker respond in over an hour. This points to tracker or network problems.
moving_storage:true OR state:checking_files,checking_resume_dataTorrents busy with disk work; either being moved or rechecked.
flags:paused,~auto_managedTorrents that have been manually paused.
flags:sequential_download state:downloadingDownloads in sequential mode, usually because streaming.

Stalled and dead​

QueryDescription
state:downloading flags:~paused download_payload_rate:0 (last_download:>1h OR -has:last_download) added_time:<-1hStalled downloads: nothing received for an hour, or ever. The added_time filter skips torrents that were only just added.
progress:>=95 is_finished:false flags:~pausedDownloads stuck nere the end, often waiting for the last (rare) pieces.
is_finished:false flags:~paused num_seeds:0 (last_seen_complete:<-14d OR -has:last_seen_complete)Likely dead torrents: no connected seeds, and no complete copy seen in two weeks, or ever.

Seeding and cleanup​

QueryDescription
is_finished:true ratio_real:<1 seeding_duration:<3dPrivate-tracker hit-and-run protection: finished torrents that haven't paid back their download yet and have seeded less than three days. Don't remove these. It uses ratio_real so a cross-seed doesn't hide a real deficit.
is_seeding:true (last_upload:>30d OR -has:last_upload) added_time:<=30dSeeds nobody has downloaded from in a month, or ever, that have been around at least that long. These are good candidates for freeing space.
is_seeding:true (ratio:>=2 OR seeding_duration:>4w) -$userdata.tags:keepTorrents that have done their part (ratio 2 or a month of seeding), excluding anything tagged keep. A typical auto-removal rule.

Fields​

General and tracker​

FieldKindDescriptionExample
errcBoolTrue if the torrent is in an error state.errc:true
save_pathTextDirectory where the torrent data is stored.save_path:/mnt/media
nameTextName of the torrent. Plain values match substrings, = matches the whole name, * is a glob.name:=*.iso
next_announceDurationTime until the next tracker announce. Empty when none is scheduled.next_announce:<5m
current_trackerTextURL of the tracker currently in use. Empty if none has responded.current_tracker:example.org

Transfer totals (sizes)​

FieldKindDescriptionExample
total_downloadSizeBytes downloaded this session, including protocol overhead.total_download:>1gb
total_uploadSizeBytes uploaded this session, including protocol overhead.total_upload:>500mb
total_payload_downloadSizeBytes of torrent data downloaded this session, excluding overhead.total_payload_download:>1gib
total_payload_uploadSizeBytes of torrent data uploaded this session, excluding overhead.total_payload_upload:>10gb
total_failed_bytesSizeBytes downloaded that failed the hash check.total_failed_bytes:>0
total_redundant_bytesSizeBytes downloaded more than once, or thrown away.total_redundant_bytes:>100mb
total_doneSizeBytes of verified data on disk, including files that where not selected.total_done:>2gb
totalSizeTotal size of the torrenttotal:1gb..10gb
total_wanted_doneSizeBytes of verified data for the selected files.total_wanted_done:>1gb
total_wantedSizeTotal size of the selected files.total_wanted:<700mb
total_wanted_remainingSizeBytes left to download for the selected files.total_wanted_remaining:<100mib
all_time_uploadSizeTotal bytes uploaded for this torrent, across restarts.all_time_upload:>1tb
all_time_downloadSizeTotal bytes downloaded for this torrent, across restarts.all_time_download:>50gb

Dates​

Use YYYY-MM-DD, YYYY-MM-DDTHH:MM, or relative times like -1w; a bare relative time means "within the last ...".

FieldKindDescriptionExample
added_timeDateWhen the torrent was added to the session.added_time:-7d
completed_timeDateWhen the torrent finished downloading. Empty if it hasn't finished.completed_time:2026-10-01..2026-10-05
last_seen_completeDateLast time a complete copy was seen in the swarm.last_seen_complete:<-30d

Progress and queue​

FieldKindDescriptionExample
progressPercentDownload progress as a percentage from 0 to 100.progress:>=90
queue_positionNumberPosition in the libtorrent download queue. Empty for torrents that aren't queued, such as seeds.queue_position:<3

Rates​

Bytes per second; units such as kb, mib with an optional /s are supported.

FieldKindDescriptionExample
download_rateRateCurrent download speed, including overhead.download_rate:>1mb/s
upload_rateRateCurrent upload speed, including overhead.upload_rate:>500kb/s
download_payload_rateRateCurrent download speed, excluding overhead.download_payload_rate:>0
upload_payload_rateRateCurrent upload speed, excluding overhead.upload_payload_rate:>100kib/s

Peers and swarm​

FieldKindDescriptionExample
num_seedsNumberNumber of connected peers that are seeding.num_seeds:>=1
num_peersNumberNumber of connected peers, including seeds.num_peers:0
num_completeNumberNumber of seeds reported by the tracker.num_complete:<3
num_incompleteNumberNumber of downloaders reported by the tracker.num_incomplete:>50
list_seedsNumberNumber of known seeds, connected or not.list_seeds:>10
list_peersNumberNumber of known peers, connected or not.list_peers:>100
connect_candidatesNumberNumber of known peers that can still be connected to.connect_candidates:0
num_piecesNumberNumber of pieces that has been downloaded and verified.num_pieces:>1000
block_sizeNumberThe size of a block, in bytes, as requested from peers.block_size:16384
num_uploadsNumberNumber of peers currently unchoked.num_uploads:>4
num_connectionsNumberNumber of open peer connections, including half-open ones.num_connections:>50

Limits and bandwidth​

FieldKindDescriptionExample
uploads_limitNumberMaximum unchoked peers for this torrent.uploads_limit:<10
connections_limitNumberMaximum peer connections for this torrent.connections_limit:<100
upload_limitNumberUpload speed limit for this torrent, or empty if unlimited.upload_limit:<1mb/s
download_limitNumberDownload speed limit for this torrent, or empty if unlimited.has:download_limit
up_bandwidth_queueNumberNumber of peers waiting for upload bandwidth.up_bandwidth_queue:>0
down_bandwidth_queueNumberNumber of peers waiting for download bandwidth.down_bandwidth_queue:>0
seed_rankNumberSeeding priority used by the auto-manager; higher ranks are seeded first.seed_rank:>1000

State and status flags​

FieldKindDescriptionExample
stateStateCurrent state: checking_files, downloading_metadata, downloading, finished, seeding or checking_resume_data.state:downloading,seeding
is_seedingBoolTrue if the torrent has all pieces and is seeding.is_seeding:true
is_finishedBoolTrue if all selected files are downloaded.is_finished:yes
has_metadataBoolTrue if the torrent has metadata (magnet links has no metadata)has_metadata:false
has_incomingBoolTrue if a peer has connected to us for this torrent.has_incoming:true
moving_storageBoolTrue while the torrent's files are being moved to a new path.moving_storage:true
announcing_to_trackersBoolTrue if the torrent announces to its trackers.announcing_to_trackers:false
announcing_to_lsdBoolTrue if the torrent announces to Local Service Discovery.announcing_to_lsd:true
announcing_to_dhtBoolTrue if the torrent announces to the DHT.announcing_to_dht:false
info_hashHashThe torrent's v1 or v2 info hash. Any hash prefix of 4-64 hex characters will match.info_hash:a1b2c3d4

Activity durations​

Duration fields accepts suffixes s, m, h, d, w, mo, y.

FieldKindDescriptionExample
last_uploadDurationTime since data was last uploaded. Empty if never.last_upload:>7d
last_downloadDurationTime since data was last downloaded. Empty if never.last_download:>1d
active_durationDurationTotal time the torrent has been active (not paused).active_duration:>30d
finished_durationDurationTotal time the torrent has been in the finished state.finished_duration:>1w
seeding_durationDurationTotal time the torrent has been seeding.seeding_duration:>2w
flagsFlagsThe libtorrent torrent flags. Lists all matched flags. ~ negates.flags:auto_managed,~paused

Porla fields​

Fields that are useful but not part of the libtorrent torrent status.

FieldKindDescriptionExample
etaDurationEstimated time left based on the remaining selected bytes and the current download speed. Empty if finished or no download speed.eta:<1h
ratioNumberUpload ratio (all-time upload ÷ all-time download). Uses the data held on disk instead when the download counter is missing or very small, e.g for seed-mode or cross-seeded torrents. Capped at 9999.ratio:>=2
ratio_realNumberUpload ratio from this client's own counters only, without any fallback.ratio_real:<1
$userdata.categoryTextThe torrent's category, or empty if not set.$userdata.category:=sonarr
$userdata.tagsTagThe torrent's tags. Matches if any tag equals a listed value; * globs work.$userdata.tags:hd,4k

Syntax​

A query is a list of terms. Terms next to each other must all match.

SyntaxMeaning
a b, a AND b, a && bboth a and b
a OR b, a || beither a or b
-a, !a, NOT anot a
( ... )grouping
field:valuecompare a field, see Fields
wordfree text, see Free text
"some words"an exact phrase. Use \" for a quote inside it

NOT binds tighter than AND, which binds tighter than OR. So debian OR ubuntu server means debian OR (ubuntu AND server).

warning

The keywords AND, OR and NOT must be written in upper case. A lower case and or or is searched for as text, so rock and roll finds names containing all three words.

Free text​

A word or a quoted phrase without a field searches the torrent name. The search ignores case for ASCII letters (ubuntu matches Ubuntu) but not for other letters (pårla does not match Pårla).

A word of 8 to 64 hexadecimal characters also matches torrents whose v1 or v2 info hash starts with it.

Some characters have a meaning in PQL: ( ) " < > = ! : and a leading -. Quote a search that contains them, for example "http://example.com" or "hello!".

Comparing fields​

Write a field name, a colon and a value, with no spaces: total:>1gb. An optional operator goes between the colon and the value.

OperatorMeaning
nonecontains (text), equals (everything else)
=equals
>greater than
>=greater than or equal to
<less than
<=less than or equal to

Values can also be written as:

FormMeaningWorks for
a,b,cany of these valuestext, tag, hash, is, has
a..ba range, including both endsnumbers, sizes, rates, durations, percentages, dates
ubuntu*isoa pattern where * matches anything. It must match the whole valuetext, tag
"a,b"the literal value, with no lists, ranges or patternseverything

A field that has no value for a torrent never matches. For example eta:<1h never matches a torrent without a known ETA, and -eta:<1h always does.