yoktez
Typed Python client for the National Thesis Center of Turkey.
yoktez wraps the YOK NTC JSP/AJAX surface behind a single synchronous Client with frozen-dataclass return types, a deterministic exception hierarchy, and bilingual-aware fields. Built for application and CLI developers who need a typed surface and a small install footprint without writing bespoke scraping code for each project.
Installation
pip install yoktez
Requires Python 3.14+.
Quickstart
"""End-to-end yoktez quickstart: search -> metadata -> assets.
Demonstrates the typical three-call flow without writing files to disk.
Run with: `python examples/quickstart.py`
"""
from yoktez import AssetStatus, Client
_QUERY = "yapay zeka"
with Client() as client:
results = client.search.simple(_QUERY)
print(f"{results.total} matches for {_QUERY!r}")
thesis = results[0]
print(f" title: {thesis.title}")
print(f" author: {thesis.author}")
print(f" year: {thesis.year}")
print(f" keys: {thesis.registration_no} / {thesis.thesis_no}")
metadata = client.metadata.get(thesis)
print(f" advisor: {metadata.supervisor}")
if metadata.affiliation is not None:
print(f" uni: {metadata.affiliation.university}")
if metadata.keywords is not None:
print(f" tags: {len(metadata.keywords)} keywords")
assets = client.assets.get(thesis)
print(f" status: {assets.status.name}")
if assets.status is AssetStatus.AVAILABLE:
print(f" pdf_key: {assets.pdf_key}")
Sample output:
6841 matches for 'yapay zeka'
title: Kimya eğitiminde yapay zekâ araştırmalarına ilişkin bir meta-sentez çalışması
author: MURAT EBUBEKİR YAYLA
year: 2026
keys: nslbSyAODG1_FIruL8qUAA / THvIvDpZXvJIiHZpuqpKVw
advisor: PROF. DR. MUSA ÜCE
uni: MARMARA ÜNİVERSİTESİ
tags: 5 keywords
status: AVAILABLE
pdf_key: 5T1_CZ5-UGb9QCmoURec4AbpuuyvqUeed_1PcCh_6DVZ4b1fbX7Gcu-DQFLIcE11
Features
- Four search modes:
simple,advanced,detail, andrecentfrom a singleclient.searchnamespace, all returning a sliceableSearchResultscarrying the database-wide match total alongside the result window. - Structured metadata:
client.metadata.get(thesis)returns a typedThesisMetadatawith bilingual keywords (Bilingual(raw, tr, en)), a tieredAffiliation, and pre-formatted citation strings (APA / IEEE / MLA / Chicago / Harvard). - Two-step asset download:
client.assets.get(thesis)resolves to one ofAVAILABLE/UNDER_EMBARGO/NO_PERMIT/PREPARINGbefore any bytes move; the available branch exposes apdf_key(and optionalappendix_key) to feeddownload_pdf/download_appendix. - Catalog lookups:
client.lookupscovers universities (TR / INT), institutes, divisions, subjects, departments, sections, and keywords, with per-instance memoization and an explicitrefresh(). - Typed value objects: every returned record is a
@dataclass(frozen=True, slots=True); values are immutable, hashable where field types allow, and ship withpy.typedfor downstream type checkers. - Sync-only, thread-friendly: no
async/awaitsurface; the recommended concurrency pattern is oneClientper thread. - Small dependency surface:
httpx,beautifulsoup4, andlxml. No Rust core, no auth, no hidden state.
Usage
All snippets assume with Client() as client: for deterministic cleanup of the underlying HTTP connection pool.
Search
Simple search by free text, optionally narrowed to a single field:
from yoktez import Client, SearchField
with Client() as client:
results = client.search.simple("yapay zeka", field=SearchField.ABSTRACT)
print(f"{results.total} matches")
for thesis in results[:5]:
print(thesis.year, thesis.title)
Advanced search joins up to three terms with boolean operators:
from yoktez import AdvancedOperator, Client, MatchType
with Client() as client:
results = client.search.advanced(
"sosyal",
term2="medya",
op1=AdvancedOperator.AND,
match=MatchType.INCLUDES,
)
Detail search accepts the full filter surface; enum-shaped parameters also accept the member name as a string or the raw int code:
from yoktez import Client, ThesisType
with Client() as client:
unis = client.lookups.universities()
results = client.search.detail(
university=unis[0],
year_min=2020,
year_max=2025,
degree_type=ThesisType.MASTER, # also accepts "MASTER" or 1
)
Recently added theses (server-fixed 15-day window):
from yoktez import Client
with Client() as client:
results = client.search.recent()
Metadata
from yoktez import Client
with Client() as client:
thesis = client.search.simple("makine öğrenmesi")[0]
metadata = client.metadata.get(thesis)
if metadata.affiliation is not None:
print(metadata.affiliation.university)
if metadata.keywords:
print(metadata.keywords[0].tr, "=", metadata.keywords[0].en)
if metadata.references is not None:
print(metadata.references.apa)
Assets (two-step download)
from yoktez import AssetStatus, Client
with Client() as client:
thesis = client.search.simple("yapay zeka")[0]
assets = client.assets.get(thesis)
if assets.status is AssetStatus.AVAILABLE and assets.pdf_key is not None:
client.assets.download_pdf(assets.pdf_key, "thesis.pdf")
if assets.appendix_key is not None:
client.assets.download_appendix(assets.appendix_key, "thesis-ek.rar")
download_pdf and download_appendix accept a filesystem path (Path or str, opened and closed for you) or a pre-opened binary file-like (written to but not closed — ownership stays with the caller).
Lookups
from yoktez import Client, UniversitySource
with Client() as client:
unis = client.lookups.universities(UniversitySource.TR)
institutes = client.lookups.institutes(unis[0])
divisions = client.lookups.divisions(unis[0], institutes[0])
# Bulk catalogs; keywords() also accepts group / language / first_letter / search.
keywords = client.lookups.all_keywords()
Every client.lookups.* call is memoized on the Client instance. Call client.lookups.refresh() to clear the cache if YOKSIS IDs are suspected to have rotated.
HTTP client configuration
Client accepts keyword-only overrides for the underlying httpx.Client:
from yoktez import Client
with Client(timeout=60, retries=5, user_agent="my-app/1.0") as client:
...
For full control, inject a pre-built httpx.Client via http_client=. Ownership stays with the caller; Client.close() is a no-op for an injected client:
import httpx
from yoktez import Client
http = httpx.Client(timeout=30.0, follow_redirects=True)
try:
with Client(http_client=http) as client:
...
finally:
http.close()
Concurrency
yoktez.Client is single-threaded by design — share one per thread, never across threads. The library ships no concurrency primitives; threading strategy is the caller's choice.
Design principles
- Synchronous-only API: Sync is sufficient for YOK NTC's IO patterns; an async surface would double the API and complicate testing for no proven benefit. Concurrency strategy belongs to the caller, and
examples/multithreaded_pool.pydemonstrates the one-Client-per-thread pattern. - Frozen-dataclass value objects: Every returned record is
@dataclass(frozen=True, slots=True). Stdlib-only, immutable, hashable, and very fast. - Coerce-on-input enum handling: Enum-shaped parameters accept the matching
Enummember, its name (e.g.,"MASTER"), or its raw int code; the raw-intpassthrough tolerates new YOK NTC codes the library hasn't yet enumerated, so wire-side additions don't gate a release. - Two-step download flow:
client.assets.get(...)resolves status first;download_pdfanddownload_appendixrun only on the available branch. Honest to the underlying YOK NTC flow, and lets callers inspect embargo dates and appendix availability before committing to a second request. - Hierarchical logger naming: Every sub-package logs under
yoktez.<concern>(yoktez.http,yoktez.search,yoktez.lookups,yoktez.assets). Operators can silence the high-volume HTTP DEBUG channel while preserving the rarer parser WARNING channels; a singlelogging.getLogger("yoktez").setLevel(...)still catches every child through parent propagation.
Limitations
yoktez is intentionally narrow. The following are out of scope and will not land in this package:
- No async API: Synchronous code throughout; no
async def, no asyncio surface. - No multi-threaded helper functions: Concurrency strategy is the caller's choice.
- No authentication or login flows (e-Devlet): Anonymous public-data access only; features requiring login (favorites, history) are excluded.
- No bypassing access restrictions: Embargoed and no-permit theses surface their state via
AssetStatusand the matching exception types; the library does not attempt to circumvent these. - No data hosting or mirroring: The library fetches on demand; no bundled snapshots of the YOK NTC database.
- No CLI shipped from this package: A separate package may add one later — out of scope here.
License
MIT — see LICENSE.
Metadata
Release files for yoktez 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| yoktez-0.2.0.tar.gz | 379.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| yoktez-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 417.6 kB
Release files / yoktez-0.2.0.tar.gz
| Download URL | yoktez-0.2.0.tar.gz |
|---|---|
| Size | 379.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b07af113cf928dfd4a931c343d2e8336b3ca8d4bf3cfb6bfd37ac54ab6a3f0e8
|
|
BLAKE2b-256 checksum How to use checksums |
9e6113974b07f95418cdfdf3a712560d2259030baebf94ef21c11f77ebd47269
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 21, 2026.
Transparency logRelease files / yoktez-0.2.0-py3-none-any.whl
| Download URL | yoktez-0.2.0-py3-none-any.whl |
|---|---|
| Size | 38.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
90376d452e1e805cda30afaba9b930a5418971b3c6b0bf04c04b4531a3be100c
|
|
BLAKE2b-256 checksum How to use checksums |
f0f4f87499db3e87974f8293e85d74c866df136dd9a816119e3436f3181b0c20
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 21, 2026.
Transparency log