tezrThis vignette walks through the
core features of tezr. Each section builds on the previous
one, starting with simple keyword searches and progressing to
multi-filter queries, detail retrieval, and cache management.
The vignette does not run live NTC queries during ordinary package
builds. It uses representative output so readers can see the returned
shapes without depending on the live portal. Set
TEZR_LIVE_EXAMPLES=true before rendering if you want to
execute the portal requests.
tezr has three search functions:
search_basic(), search_advanced(), and
search_detailed(). The sections below cover each function
in order.
Use tezr for academic, reproducible research workflows.
Cache or save large results locally, avoid repeated identical requests,
and respect the NTC portal’s access controls and terms. Do not post
private researcher data, access tokens, local cookies, or sensitive
institutional details in issues or pull requests.
When publishing results, cite both tezr and the National
Thesis Center or Council of Higher Education data source. Record the
query terms, filters, retrieval date, and any completeness warnings
returned by the package.
tezr does more than submit a web form and return rows.
It resolves lookup labels, filters searches through structured portal
fields, paginates capped result sets by adaptive year ranges, parses
bilingual metadata, deduplicates expanded searches, annotates results
with completeness attributes, caches repeated requests, and exposes
statistics tables that help check retrieval completeness.
search_basic() searches the National Thesis Center (NTC) database
by keyword. It checks all fields by default, so it works well when you
do not know where your term appears.
The output is a tibble with one row per thesis.
#> Rows: 8
#> Columns: 13
#> $ thesis_no <chr> "967755", "975988", "955779", "974976", "960162", "9…
#> $ title_original <chr> "Tarimsal sulama verimliligi 1", "Tarimsal sulama ve…
#> $ title_translation <chr> "Agricultural irrigation efficiency 1", "Agricultura…
#> $ author <chr> "PERIHAN EZGI BALLI", "CENAP ALAYBEYI", "SECIL ALMIS…
#> $ university <chr> "Bandirma Onyedi Eylul Universitesi", "Harran Univer…
#> $ year <int> 2025, 2025, 2025, 2024, 2023, 2022, 2021, 2020
#> $ thesis_type_tr <chr> "Doktora", "Yuksek Lisans", "Yuksek Lisans", "Yuksek…
#> $ thesis_type_en <chr> "Doctorate", "Master", "Master", "Master", "Doctorat…
#> $ language_tr <chr> "Turkce", "Turkce", "Turkce", "Turkce", "Turkce", "I…
#> $ language_en <chr> "Turkish", "Turkish", "Turkish", "Turkish", "Turkish…
#> $ subject_tr <chr> "Ziraat; Ekonomi", "Ziraat; Ekonomi", "Ziraat; Ekono…
#> $ subject_en <chr> "Agriculture; Economics", "Agriculture; Economics", …
#> $ detail_id <chr> "TCKf4ksTOVsOBqUcPYMKWQ", "LypZzbdoWcG0f3c62wverw", …
You can use the search_field argument to restrict
matching to a single field.
# Search only in thesis titles
ag_irrigation_title <- search_basic(
"tarımsal sulama",
search_field = "title")
dplyr::glimpse(ag_irrigation_title)#> Rows: 3
#> Columns: 13
#> $ thesis_no <chr> "967755", "975988", "955779"
#> $ title_original <chr> "Tarimsal sulama verimliligi 1", "Tarimsal sulama ve…
#> $ title_translation <chr> "Agricultural irrigation efficiency 1", "Agricultura…
#> $ author <chr> "PERIHAN EZGI BALLI", "CENAP ALAYBEYI", "SECIL ALMIS"
#> $ university <chr> "Bandirma Onyedi Eylul Universitesi", "Harran Univer…
#> $ year <int> 2025, 2025, 2025
#> $ thesis_type_tr <chr> "Doktora", "Yuksek Lisans", "Yuksek Lisans"
#> $ thesis_type_en <chr> "Doctorate", "Master", "Master"
#> $ language_tr <chr> "Turkce", "Turkce", "Turkce"
#> $ language_en <chr> "Turkish", "Turkish", "Turkish"
#> $ subject_tr <chr> "Ziraat; Ekonomi", "Ziraat; Ekonomi", "Ziraat; Ekono…
#> $ subject_en <chr> "Agriculture; Economics", "Agriculture; Economics", …
#> $ detail_id <chr> "TCKf4ksTOVsOBqUcPYMKWQ", "LypZzbdoWcG0f3c62wverw", …
Available search field values are: "all" (default),
"title", "author", "supervisor",
"subject", "index", "abstract",
and "thesis_no".
search_basic() also accepts thesis_type and
access_type filters. These filters are applied server-side,
so you download fewer records.
Available thesis_type values are: "all"
(default), "masters", "phd",
"medical_specialty", "arts",
"dentistry", "medical_sub",
"pharmacy".
# PhD dissertations only
phd_results <- search_basic(
"ekonometri",
thesis_type = "phd")
dplyr::glimpse(phd_results)#> Rows: 4
#> Columns: 13
#> $ thesis_no <chr> "967755", "960162", "946580", "968778"
#> $ title_original <chr> "Ekonometri uygulamalari 1", "Ekonometri uygulamalar…
#> $ title_translation <chr> "Applications of econometrics 1", "Applications of e…
#> $ author <chr> "PERIHAN EZGI BALLI", "SAMI KAYA", "ADIL MAMMADOV", …
#> $ university <chr> "Bandirma Onyedi Eylul Universitesi", "Istanbul Univ…
#> $ year <int> 2025, 2023, 2022, 2020
#> $ thesis_type_tr <chr> "Doktora", "Doktora", "Doktora", "Doktora"
#> $ thesis_type_en <chr> "Doctorate", "Doctorate", "Doctorate", "Doctorate"
#> $ language_tr <chr> "Turkce", "Turkce", "Ingilizce", "Turkce"
#> $ language_en <chr> "Turkish", "Turkish", "English", "Turkish"
#> $ subject_tr <chr> "Ekonometri; Ekonomi", "Ekonometri; Ekonomi", "Ekono…
#> $ subject_en <chr> "Econometrics; Economics", "Econometrics; Economics"…
#> $ detail_id <chr> "TCKf4ksTOVsOBqUcPYMKWQ", "h8m0hAyVIXV5l0pzt6sd8A", …
Available access type values are: "all" (default),
"open", "restricted".
Basic search cannot exceed 2000 results. This is a server-side limit.
If your query returns more than 2000 records, the function warns you. In
these cases, you can set max_search_results = Inf to
paginate past the limit. search_basic() automatically
delegates to advanced search for pagination when you set
max_search_results = Inf. There is more information about
pagination below.
search_advanced() adds year range, language, group,
university/institute, and thesis status filters to keyword search.
The NTC advanced search form supports up to three keyword rows
combined with Boolean operators (AND, OR,
NOT), each targeting a different field.
search_advanced() exposes only the first keyword row.
R packages that interface with academic databases, such as rentrez (PubMed) and europepmc (Europe PMC),
often pass Boolean logic as a single query string (for example,
"term1 AND term2"). NTC does not accept free-form Boolean
strings. It uses structured form fields for each keyword row, so that
pattern is not applicable here. To keep the interface simple,
search_advanced() does not expose Boolean row combinations.
For equivalent results, you can use the following approaches.
AND: Use search_detailed() with its
field-specific parameters (title, author,
supervisor, etc.).
OR: Run separate searches and combine with
dplyr::bind_rows() |> dplyr::distinct().
NOT: Run both searches and exclude with
dplyr::anti_join().
# Keyword search with year range
recent_climate <- search_advanced(
keyword = "iklim değişikliği",
year_start = 2015,
year_end = 2024
)
# English-language theses only
# language accepts ISO 639 codes ("tr", "en", "fr", "de", ...), or
# full names ("Turkish", "French")
english_growth <- search_advanced(
keyword = "economic growth",
language = "en"
)
# French-language theses
french_theses <- search_advanced(
keyword = "migration",
language = "fr"
)group limits results to a broad group:
"all" (default), "science" (Fen Bilimleri),
"social" (Sosyal Bilimler), "medical" (Tıp ve
Sağlık Bilimleri).
status controls whether results include only approved
theses or also in-preparation ones: "approved" (default),
"all", "in_preparation".
You can combine filters to build precise keyword queries. Start with a minimal query, then add constraints.
When max_search_results is greater than 2000 (including
Inf) and the server reports more than 2000 matches,
tezr switches to iterative year-range pagination. If you do
not supply year_start and year_end, the
package uses 1959:current_year as the search window. It
then creates year chunks with weighted split points (pre-2000,
2000-2010, post-2010) and a safety target below the hard 2000-row cap.
Each chunk is requested with the same filters as the original query. If
a chunk is still capped by the server limit, that chunk is split again
and retried until the range is small enough (or a single year remains).
During this process, tezr updates split weights from
observed uncapped chunk densities to bias later splits toward denser
periods. Finally, chunk results are merged, deduplicated by
thesis_no, and returned. If a single year still exceeds
2000 results, the package cannot paginate further for that year and
warns you to narrow the query with additional filters.
# Retrieve all results (auto-paginate by year)
all_eu <- search_advanced(
keyword = "avrupa",
search_field = "all",
year_start = 2018,
year_end = 2020,
max_search_results = Inf
)
dplyr::glimpse(all_eu)#> Rows: 8
#> Columns: 13
#> $ thesis_no <chr> "967755", "975988", "955779", "974976", "960162", "9…
#> $ title_original <chr> "Avrupa Birligi ve Turkiye 1", "Avrupa Birligi ve Tu…
#> $ title_translation <chr> "European Union and Turkiye 1", "European Union and …
#> $ author <chr> "PERIHAN EZGI BALLI", "CENAP ALAYBEYI", "SECIL ALMIS…
#> $ university <chr> "Bandirma Onyedi Eylul Universitesi", "Harran Univer…
#> $ year <int> 2025, 2025, 2025, 2024, 2023, 2022, 2021, 2020
#> $ thesis_type_tr <chr> "Doktora", "Yuksek Lisans", "Yuksek Lisans", "Yuksek…
#> $ thesis_type_en <chr> "Doctorate", "Master", "Master", "Master", "Doctorat…
#> $ language_tr <chr> "Turkce", "Turkce", "Turkce", "Turkce", "Turkce", "I…
#> $ language_en <chr> "Turkish", "Turkish", "Turkish", "Turkish", "Turkish…
#> $ subject_tr <chr> "Avrupa Birligi; Ekonomi", "Avrupa Birligi; Ekonomi"…
#> $ subject_en <chr> "European Union; Economics", "European Union; Econom…
#> $ detail_id <chr> "TCKf4ksTOVsOBqUcPYMKWQ", "LypZzbdoWcG0f3c62wverw", …
search_detailed() provides field-specific and
institutional filters. Use it when you need to target thesis titles,
authors, supervisors, universities, divisions, disciplines, or subjects.
It supports the same auto-pagination flow as
search_advanced().
Available parameters in search_detailed() are:
thesis_no, title, author,
supervisor, abstract, keyword,
university, university_id,
institute, institute_id,
division, division_id, subject,
discipline, discipline_id,
thesis_type, year_start,
year_end, language, access_type,
group, status,
max_search_results, ignore_cache.
You should provide at least one search criterion or filter to use the function.
You can use the list_*() functions to discover valid
filter names from the server. In normal use, you can pass names (for
example university = "Ankara Üniversitesi") into functions,
and tezr will handle ID mapping internally. The
id columns are mainly lookup keys used by tezr
but you can also pass IDs directly for advanced or performance-sensitive
workflows (for example university_id,
institute_id, division_id,
discipline_id) if you want.
#> # A tibble: 3 × 2
#> name id
#> <chr> <chr>
#> 1 ANKARA UNIVERSITESI 2
#> 2 ISTANBUL UNIVERSITESI 3
#> 3 ORTA DOGU TEKNIK UNIVERSITESI 60
# Subjects have Turkish and English names
subjects <- list_subjects()
subjects |>
filter(stringr::str_detect(name_tr, stringr::fixed("Ekonomi")))#> # A tibble: 1 × 3
#> name_tr name_en id
#> <chr> <chr> <chr>
#> 1 Ekonomi Economics 115
You can pass university, institute, or division names as strings.
tezr resolves them to internal IDs automatically via lookup
as we mentioned above.
Subjects are broad categories (e.g., “Ekonometri”) whereas
disciplines are specializations (e.g., “İktisat Teorisi”). You can use
list_subjects() and list_disciplines() to
confirm exact names.
You can also filter results by supervisor names.
# Find theses supervised by a specific supervisor
supervisor_theses <- search_detailed(supervisor = "Mustafa Kadir Doğan")
head(supervisor_theses)#> # A tibble: 6 × 13
#> thesis_no title_original title_translation author university year
#> <chr> <chr> <chr> <chr> <chr> <int>
#> 1 9677551 Parasal aktarım mekanizma… Domestic credit … PERIH… Ankara Un… 2020
#> 2 9759882 1980 sonrası Türkiye'de a… Changes in sunfl… CENAP… Ankara Un… 2021
#> 3 9557793 Doğrudan yabancı yatırıml… Foreign direct i… SECIL… Ankara Un… 2022
#> 4 9749764 Enerji kullanımı ile G-20… Panel data analy… RUMEY… Ankara Un… 2023
#> 5 9601625 Finansal liberalleşme sür… Fragility during… SAMI … Ankara Un… 2024
#> 6 9465806 Azerbaycan'ın ekolojik ay… An analysis of A… ADIL … Ankara Un… 2025
#> # ℹ 7 more variables: thesis_type_tr <chr>, thesis_type_en <chr>,
#> # language_tr <chr>, language_en <chr>, subject_tr <chr>, subject_en <chr>,
#> # detail_id <chr>
The YÖK web portal accepts only one value per filter field.
tezr removes this restriction. Most
search_detailed() parameters accept character vectors. When
you pass multiple values, the package expands them into separate API
calls, combines the results, and deduplicates by thesis_no.
This makes cross-institutional and cross-disciplinary comparisons
possible in a single function call.
# Search across multiple universities
multi_uni <- search_detailed(
university = c("Ankara Üniversitesi", "İstanbul Üniversitesi"),
subject = "Ekonomi"
)
# Search multiple disciplines within a subject
multi_discipline <- search_detailed(
subject = "Ekonomi",
discipline = c("İktisat", "Maliye", "Ekonometri")
)
# Multiple thesis types
multi_type <- search_detailed(
subject = "Ekonomi",
thesis_type = c("phd", "masters")
)
# Multiple languages (ISO 639 codes)
multi_lang <- search_detailed(
subject = "Ekonomi",
language = c("tr", "en", "fr")
)
# Search across multiple universities with pagination
multi_uni_all <- search_detailed(
university = c("Ankara Üniversitesi", "İstanbul Üniversitesi"),
subject = "Ekonomi",
max_search_results = Inf,
ignore_cache = TRUE
)Search results contain core metadata (title, author, university,
year, type, subject). If you need full details, such as abstracts,
keywords, supervisor names, page counts, and PDF links, you can use
detail() function.
You can pass a single detail_id from search results to
detail(). The function returns a one-row tibble.
# Search and get details for the first match
ankara_econ <- search_detailed(
university = "Ankara Üniversitesi",
division = "İktisat Ana Bilim Dalı",
thesis_type = "phd",
year_start = 2024,
year_end = 2025
)
ankara_econ_details <- detail(ankara_econ$detail_id[2])
dplyr::glimpse(ankara_econ_details)
# English abstract
ankara_econ_details$abstract_translation#> Rows: 1
#> Columns: 24
#> $ thesis_no <chr> "9677551"
#> $ title_original <chr> "Parasal aktarim mekanizmasi cercevesinde ozel se…
#> $ title_translation <chr> "Domestic credit to private sector and monetary t…
#> $ author <chr> "PERIHAN EZGI BALLI"
#> $ advisor <chr> "PROF. DR. HASAN SAHIN"
#> $ co_advisor <chr> NA
#> $ university <chr> "Ankara Universitesi"
#> $ institute <chr> "Sosyal Bilimler Enstitusu"
#> $ division <chr> "Iktisat Ana Bilim Dali"
#> $ year <chr> "2020"
#> $ pages <chr> "153"
#> $ thesis_type_tr <chr> "Doktora"
#> $ thesis_type_en <chr> "Doctorate"
#> $ language_tr <chr> "Turkce"
#> $ language_en <chr> "Turkish"
#> $ subject_tr <chr> "Ekonomi; Enerji"
#> $ subject_en <chr> "Economics; Energy"
#> $ abstract_original <chr> "Enerji piyasasi duzenlemelerinin ana ekseninde e…
#> $ abstract_translation <chr> "This thesis consists of three essays on energy m…
#> $ keywords_tr <chr> "Enerji piyasalari; Duzenleme; Elektrik"
#> $ keywords_en <chr> "Energy markets; Regulation; Electricity"
#> $ access_status <chr> "open"
#> $ pdf_url <chr> "https://tez.yok.gov.tr/UlusalTezMerkezi/tezIndir…
#> $ detail_url <chr> "https://tez.yok.gov.tr/UlusalTezMerkezi/tezDetay…
#> [1] "This thesis consists of three essays on energy market regulation, market design, and transformation."
You can also pass a vector of detail_id values to fetch
details for multiple theses. The function shows text progress updates by
default and fetches uncached records in parallel (up to 5 active
requests).
These functions return summary statistics tables from the NTC.
#> # A tibble: 5 × 4
#> year yuksek_lisans doktora toplam
#> <int> <int> <int> <int>
#> 1 2021 28906 7128 36034
#> 2 2022 33102 7481 40583
#> 3 2023 35987 8026 44013
#> 4 2024 40124 8841 48965
#> 5 2025 22615 4416 27031
tezr caches search results, detail records, year-range
queries, and lookup lists in memory. Caching speeds up repeated queries
and reduces server load.
#> $enabled
#> [1] TRUE
#>
#> $search_count
#> [1] 0
#>
#> $range_count
#> [1] 0
#>
#> $detail_count
#> [1] 0
#>
#> $search_ttl
#> [1] 3600
#>
#> $detail_ttl
#> NULL
The output includes search_count,
range_count, detail_count,
search_ttl, and detail_ttl. Search cache
defaults to 3600 seconds (1 hour). Detail cache defaults to
NULL (session lifetime; entries stay until you clear them
or restart R).
You can clear specific cache types or everything at once. The
"lookups" option clears cached university/subject/division
lists.
You can also adjust time-to-live settings or disable caching
entirely. TTL values are in seconds. NULL means entries
persist for the entire session.
tezr identifies itself with a package-specific user
agent by default. You can override that value for an institutional
network policy or a portal compatibility issue.
request_config(user_agent = "[email protected]")
request_config(reset = TRUE)Set request_config(verbose = FALSE) or
TEZR_VERBOSE=false to silence informational progress
messages. Warnings and errors are still shown.
The package applies a two-second request delay, retries requests up to three times, refreshes sessions after 50 logical requests or 20 minutes, and uses in-memory caches for repeated searches, range chunks, details, and lookups. Detail requests fetch uncached records in bounded parallel batches.
Search results are returned in tibbles, so they work directly with
dplyr and other tidyverse tools.
climate_change <- search_basic("climate change")
# Count by year
climate_change |>
dplyr::count(year)
# Filter recent PhDs
climate_change |>
dplyr::filter(thesis_type_tr == "Doktora", year >= 2020) |>
dplyr::select(author, year, title_original, university)
# Most common subjects
climate_change |>
dplyr::count(subject_tr, sort = TRUE) |>
dplyr::slice_head(n = 10)See the Analysis Examples vignette for complete analysis workflows with visualizations.
tezr scrapes
<tez.yok.gov.tr> by simulating browser requests and parsing
HTML/JavaScript responses. Any change to the portal’s page structure,
form parameters, or JavaScript patterns will break the package until
updated. This is the primary fragility risk.readr::write_rds() or saveRDS() for
persistence across sessions.ssl_verifypeer = FALSE). This is a security trade-off
required for the package to function.search_detailed() expands vector-valued parameters (e.g.,
multiple universities, subjects) into separate API calls via cartesian
product. Passing many multi-valued parameters can generate a large
number of requests (e.g., 10 universities × 5 subjects = 50
searches).list_*() lookup
functions use exact and substring matching. Typos or minor name
variations (for example, missing diacritics) will not match. Use
list_universities() and related lookup functions to confirm
exact names before searching.readr::write_rds(),
readr::write_csv(), or similar functions. Then reload from
disk in later sessions.max_search_results = Inf.Use citation("tezr") for the preferred package citation.
Also cite the National Thesis Center or Council of Higher Education as
the source of thesis metadata and include the date you retrieved the
data.