Skip to contents

This vignette is fully reproducible without a Scopus API key. Elsevier’s API terms do not permit redistributing retrieved records, so no package can ship a genuine Scopus harvest, and scopusflow bundles an openly licensed stand-in instead. example_records holds 138 real journal articles on graphene supercapacitors published between 2015 and 2024, with their real titles, DOIs, journals, first authors and citation counts. They come from OpenAlex, whose metadata is released under CC0, reshaped into the schema a retrieval returns. The harvest is complete rather than sampled, so its rows per year are the real number of publications per year for that query, and its gaps are genuine too. Eleven records carry no DOI and two no source title, exactly as they arrive. Running the equivalent query against Scopus yields the same kind of object, with the same columns and the same handling, though not an identical set of records. The steps that genuinely need the API are shown but not run, each paired with the offline equivalent.

Describing a search as a plan

A plan separates describing a search from executing it. Plans are inspectable, saveable and version-controllable, and they can be partitioned, for example by year, so that a large retrieval stays under the API’s start < 5000 ceiling and can be cached and resumed.

The plan below describes the search the bundled records came from, so the rest of the article follows one worked example from description to export.

plan <- scopus_plan(
  "graphene supercapacitor",
  years     = 2015:2024,
  field     = "TITLE-ABS-KEY",
  partition = "year"
)
plan
cell query date year view page_size
1 TITLE-ABS-KEY(graphene supercapacitor) 2015 2015 STANDARD 200
2 TITLE-ABS-KEY(graphene supercapacitor) 2016 2016 STANDARD 200
3 TITLE-ABS-KEY(graphene supercapacitor) 2017 2017 STANDARD 200
4 TITLE-ABS-KEY(graphene supercapacitor) 2018 2018 STANDARD 200
5 TITLE-ABS-KEY(graphene supercapacitor) 2019 2019 STANDARD 200
6 TITLE-ABS-KEY(graphene supercapacitor) 2020 2020 STANDARD 200
7 TITLE-ABS-KEY(graphene supercapacitor) 2021 2021 STANDARD 200
8 TITLE-ABS-KEY(graphene supercapacitor) 2022 2022 STANDARD 200
9 TITLE-ABS-KEY(graphene supercapacitor) 2023 2023 STANDARD 200
10 TITLE-ABS-KEY(graphene supercapacitor) 2024 2024 STANDARD 200

Each row is one query cell. Field tags wrap the query and years become a date filter.

scopus_plan("language learning", field = "TITLE")$query
[1] "TITLE(language learning)"
scopus_plan("x", years = 2015:2020)$date
[1] "2015-2020"
# A plan is a classed object, and is_scopus_plan() confirms it.
is_scopus_plan(plan)
[1] TRUE

Sizing and fetching

scopus_has_key() reports whether a key is configured, without revealing it. It is the guard the package’s own examples use to skip the steps that need the API, so it is the natural switch for a reproducible script.

[1] FALSE

With a key configured, you size a search cheaply and then execute the plan, optionally caching each cell so that an interrupted run resumes without re-spending quota. These contact the API, so they are not evaluated here.

scopus_count(
  "graphene supercapacitor",
  years = 2015:2024,
  field = "TITLE-ABS-KEY"
)

records <- scopus_fetch_plan(
  plan,
  cache_dir = scopus_cache_dir(),
  resume = TRUE
)

Without a key, the bundled corpus stands in for the result of that harvest, and the sections below run on it.

The record schema

Whether records come from the API or from the bundled corpus, they share one stable schema, so everything below would read the same on a harvest of your own. summary() takes stock of a set, and the first rows show the columns.

records <- example_records
summary(records)

[34m<scopus_records>
[39m summary
138 records, from 2015 to 2024.
90 sources, 127 with a DOI.
Cited 7015 times in total, median 24 per record.
Most frequent source: ACS Applied Materials & Interfaces.
Most cited: 
[3mGraphene for batteries, supercapacitors and beyond
[23m.
head(records)
entry_number scopus_id doi title authors year date publication citations query
1 NA 10.15541/jim20140527 Enhanced Capacitive Properties of All-solid-state Symmetric Graphene Supercapacitors by Incorporating Nitrogen-doping and SnO2 Nanoparticles Jianhua Yu 2015 2015-01-01 Journal of Inorganic Materials 1 graphene supercapacitor
2 NA NA Fabrication and Characterization of a Vertically-Oriented Graphene Supercapacitor Patrick R Rice 2015 2015-01-01 DigitalCommons - CalPoly (California State Polytechnic University) 0 graphene supercapacitor
3 NA 10.1021/am509065d Flexible and Stackable Laser-Induced Graphene Supercapacitors Zhiwei Peng 2015 2015-01-13 ACS Applied Materials & Interfaces 469 graphene supercapacitor
4 NA 10.1016/j.electacta.2015.02.019 Heavily nitrogen doped, graphene supercapacitor from silk cocoon Vikrant Sahu 2015 2015-02-04 Electrochimica Acta 195 graphene supercapacitor
5 NA 10.1002/smll.201403383 Graphene-Based Integrated Photovoltaic Energy Harvesting/Storage Device Chih-Tao Chien 2015 2015-02-19 Small 108 graphene supercapacitor
6 NA 10.1016/j.jpowsour.2015.03.015 Nanoporous graphene materials by low-temperature vacuum-assisted thermal process for electrochemical energy storage Hao Yang 2015 2015-03-05 Journal of Power Sources 47 graphene supercapacitor
# A record set is a classed tibble, and is_scopus_records() confirms the
# contract.
is_scopus_records(records)
[1] TRUE

scopus_records() produces this same shape from a raw API response, flattening the nested result into one row per record. The entry below carries the fields of a real article, one of those in the bundled corpus, in the form the API returns them.

raw <- list(entry = list(
  list(`prism:doi` = "10.1021/am509065d",
       `dc:title` =
         "Flexible and Stackable Laser-Induced Graphene Supercapacitors",
       `dc:creator` = "Zhiwei Peng",
       `prism:publicationName` = "ACS Applied Materials & Interfaces",
       `prism:coverDate` = "2015-01-13", `citedby-count` = "469")
))
scopus_records(raw, query = "TITLE-ABS-KEY(graphene supercapacitor)")
entry_number scopus_id doi title authors year date publication citations query
1 NA 10.1021/am509065d Flexible and Stackable Laser-Induced Graphene Supercapacitors Zhiwei Peng 2015 2015-01-13 ACS Applied Materials & Interfaces 469 TITLE-ABS-KEY(graphene supercapacitor)

Most frequent sources and authors

A record set already answers the first descriptive questions. scopus_top() tallies the most frequent sources or authors, counting each contributor once per record. Across these 138 articles the tally is long-tailed, as a real literature is. They are spread over 90 distinct journals, and only one, ACS Applied Materials & Interfaces, appears more than five times.

scopus_top(records, by = "source")
value n
ACS Applied Materials & Interfaces 8
Journal of Power Sources 5
Synthetic Metals 5
Electrochimica Acta 4
Journal of Materials Chemistry A 4
Scientific Reports 4
Journal of Alloys and Compounds 3
Journal of Energy Storage 3
Materials Chemistry and Physics 3
Nanotechnology 3

vignette("analysing-a-literature") covers growth trends, top-source and top-author plots and abstract retrieval in depth.

DOIs and change tracking

Extract a clean, deduplicated DOI list for import into a reference manager, and compare two retrievals to see exactly what changed. Eleven of the 138 records arrived without a DOI, so 127 come back.

dois <- scopus_extract_dois(records)
length(dois)
[1] 127
head(dois, 4)
[1] "10.15541/jim20140527"            "10.1021/am509065d"              
[3] "10.1016/j.electacta.2015.02.019" "10.1002/smll.201403383"         

A search re-run later gains records and occasionally loses one to re-indexing. Here the baseline stops at 2023 and the second pull adds the 2024 articles while dropping the first record.

baseline <- records[records$year <= 2023, ]
later <- records[-1, ]
print(scopus_diff_dois(old = baseline, new = later))

[34m<scopus_doi_diff>
[39m 14 added, 1 removed, 112 unchanged

[38;5;246m# A tibble: 127 × 2
[39m
   doi                            status
   
[3m
[38;5;246m<chr>
[39m
[23m                          
[3m
[38;5;246m<fct>
[39m
[23m 

[38;5;250m 1
[39m 10.1002/adfm.202315137         added 

[38;5;250m 2
[39m 10.1002/asia.202400548         added 

[38;5;250m 3
[39m 10.1002/slct.202302535         added 

[38;5;250m 4
[39m 10.1016/j.cej.2024.148822      added 

[38;5;250m 5
[39m 10.1016/j.diamond.2024.110842  added 

[38;5;250m 6
[39m 10.1016/j.isci.2024.111696     added 

[38;5;250m 7
[39m 10.1016/j.jallcom.2024.175000  added 

[38;5;250m 8
[39m 10.1016/j.jallcom.2024.177248  added 

[38;5;250m 9
[39m 10.1016/j.jpowsour.2024.234127 added 

[38;5;250m10
[39m 10.1016/j.jpowsour.2024.236149 added 

[38;5;246m# ℹ 117 more rows
[39m

You can write the DOIs to a path you specify, and read the file back to see exactly what lands on disk.

out <- file.path(tempdir(), "dois.csv")
scopus_extract_dois(records, file = out)
writeLines(head(readLines(out), 5))
"doi"
"10.15541/jim20140527"
"10.1021/am509065d"
"10.1016/j.electacta.2015.02.019"
"10.1002/smll.201403383"

scopus_compare_topics() measures how the internal emphasis of a literature shifts, expressed as each comparison topic’s yearly share of the reference literature. It issues one count request per term per year, so it needs the API.

cmp <- scopus_compare_topics(
  reference_query  = "language learning",
  comparison_terms = c("effect size", "Bayesian"),
  years            = 2015:2020,
  field            = "TITLE-ABS-KEY"
)
plot_scopus_comparison(cmp)

The result is a tidy table with one row per topic and year, which plot_scopus_comparison() draws with direct line labels, a colour-blind-safe palette and shaded stability bands. vignette("comparing-topics") builds the object offline, shows the plot in its variations and explains how to read the bands.

Author keywords and references

A search only returns the fields the Search API carries. Author keywords and a document’s own reference list need view = "COMPLETE" and Abstract Retrieval respectively, both at a materially different quota cost from an ordinary search. vignette("keywords-and-references") walks through both, and scopus_corpus(), which combines them into a minimal id/title/year/keywords/references shape for downstream tools.

Export and interoperability

Hand results to bibliometrix-style workflows, or save and reload them.

m <- as_bibliometrix(records)
head(m[, c("AU", "TI", "PY", "SO", "TC")])
AU TI PY SO TC
JIANHUA YU ENHANCED CAPACITIVE PROPERTIES OF ALL-SOLID-STATE SYMMETRIC GRAPHENE SUPERCAPACITORS BY INCORPORATING NITROGEN-DOPING AND SNO2 NANOPARTICLES 2015 JOURNAL OF INORGANIC MATERIALS 1
PATRICK R RICE FABRICATION AND CHARACTERIZATION OF A VERTICALLY-ORIENTED GRAPHENE SUPERCAPACITOR 2015 DIGITALCOMMONS - CALPOLY (CALIFORNIA STATE POLYTECHNIC UNIVERSITY) 0
ZHIWEI PENG FLEXIBLE AND STACKABLE LASER-INDUCED GRAPHENE SUPERCAPACITORS 2015 ACS APPLIED MATERIALS & INTERFACES 469
VIKRANT SAHU HEAVILY NITROGEN DOPED, GRAPHENE SUPERCAPACITOR FROM SILK COCOON 2015 ELECTROCHIMICA ACTA 195
CHIH-TAO CHIEN GRAPHENE-BASED INTEGRATED PHOTOVOLTAIC ENERGY HARVESTING/STORAGE DEVICE 2015 SMALL 108
HAO YANG NANOPOROUS GRAPHENE MATERIALS BY LOW-TEMPERATURE VACUUM-ASSISTED THERMAL PROCESS FOR ELECTROCHEMICAL ENERGY STORAGE 2015 JOURNAL OF POWER SOURCES 47
path <- file.path(tempdir(), "records.rds")
write_scopus_records(records, path)
identical(read_scopus_records(path), records)
[1] TRUE

Handling failures

Network and API problems surface as typed conditions, all inheriting from scopus_error, so a workflow can respond to them in code.

tryCatch(
  scopus_fetch("..."),
  scopus_error_no_key     = function(e) message("No API key configured."),
  scopus_error_rate_limit = function(e)
    message("Rate limited, so backing off."),
  scopus_error            = function(e)
    message("Scopus error: ", conditionMessage(e))
)