# Using the OData API

In the Copernicus Data Space Ecosystem (CDSE), OData (Open Data Protocol) is the primary RESTful API standard used for searching, discovering, and downloading satellite data products.

Key functions of OData in CDSE are:

- Metadata Querying: You can search for products using complex filters such as collection name (e.g., Sentinel-2), geographical area (AOI), acquisition dates, and cloud cover percentage.
- Data Download: Once a specific product ID is retrieved via a search, the OData API can be used to trigger the actual download of the data files.

This package provides convenient wrappers to access those features, and are described in this vignette.

The package also offers features for the complementary alternative catalogue via STAC. For more details on that read `vignette("STAC")`.

## Searching Products

For searching products, you could simply use `dse_odata_products()`, where you could directly add filters as arguments. But for more sophisticated queries, you can use `dse_odata_products_request()`. It creates a special class of `httr2` request object. In essence, it is a request to the API server, which you can modify with tidyverse operators. This sounds more complicated than it is.

Once you have created the request, you can add tidyverse operators (like `filter()`, `arrange()` and `slice_head()`), to modify this request. You can join those modifications with the pipe operator (`|>` or `%>%`). You can also query products that intersect with specific spatial features (`sf`) using `st_intersects()`.

Each collection will have specific fields which you can use to filter your product search. To discover which fields you use, call `dse_odata_attributes()`.

All of this is shown in the example below, where we filter products that were created after January first of 2025. Where the image overlaps with the bounding box we specify. We create a descending arrangement of identifiers, and select only the first 5 results. By calling `collect()` we ensure that the request is performed and the result is returned in a human readable form (`data.frame`).

    library(CopernicusDataspace)
    library(dplyr) # Tidiverse package for data manipulation
    library(sf)    # package for handling simple (spatial) features

    ## Define a bounding box for our search query:
    bbox <-
      st_bbox(
        c(xmin = 5.261, ymin = 52.680, xmax = 5.319, ymax = 52.715),
        crs = 4326) |>
        st_as_sfc()

    ## Create an API request:
    dse_odata_products_request() |>
      ## Filter content on ContentDate/Start`:
      filter(
        `ContentDate/Start` > "2025-01-01") |>
      ## Only data that intersects with our bounding box:
      st_intersects(bbox) |>
      ## Arrange by descending Id:
      arrange(desc(Id)) |>
      ## Only the first 5 hits:
      slice_head(n = 5) |>
      ## Collect the results:
      collect()
    #> # A tibble: 5 × 16
    #>   Id                  Name  ContentType ContentLength OriginDate PublicationDate
    #>   <chr>               <chr> <chr>               <int> <chr>      <chr>
    #> 1 ffff1bb2-405d-49c2… MCD4… applicatio…      54771356 2025-04-3… 2025-11-09T22:…
    #> 2 fffec6a0-62da-45ef… c_gl… applicatio…       2281083 2025-07-1… 2025-07-11T12:…
    #> 3 fffdd7f0-cd27-4c3e… S5P_… applicatio…      11349305 2026-05-0… 2026-05-09T13:…
    #> 4 fffd6992-b724-4a5d… c_gl… applicatio…    1080239294 2026-02-2… 2026-02-23T02:…
    #> 5 fffd6520-0f19-49fc… c_gl… applicatio…      11559794 2025-10-3… 2025-10-31T16:…
    #> # ℹ 10 more variables: ModificationDate <chr>, Online <lgl>,
    #> #   EvictionDate <chr>, S3Path <chr>, Checksum <list>, ContentDate.Start <chr>,
    #> #   ContentDate.End <chr>, Footprint <chr>, GeoFootprint.type <chr>,
    #> #   GeoFootprint.coordinates <list>

## Burst Data

Bursts is a concept associated with the Sentinel-1 C-SAR instrument. They have dedicated functions for searching for specific burst data: `dse_odata_bursts()` and `dse_odata_bursts_request()`. These are the equivalents of `dse_odata_products()` and `dse_odata_products_request()`. They work with the same mechanisms and downloading them works the same as for normal OData products, as shown below. Below a basic example of looking for and downloading of burst data:

    burst_req <-
      dse_odata_bursts_request(ParentProductId == "879d445c-2c67-5b30-8589-b1f478904269")

    ## Note that these are large files and may take a while to download:
    dse_odata_download(
      burst_req,
      tempdir()
    )

## Exploring a Product

Once you have identified an interesting product, there are ways to further explore it. You need the product identifier (ID) to do so. With `dse_odata_product_nodes()` you can see which files are associated with the product:

    dse_odata_product_nodes("c8ed8edb-9bef-4717-abfd-1400a57171a4",
                            recursive = TRUE)
    #> # A tibble: 6 × 5
    #>   Id                                Name  ContentLength ChildrenNumber Nodes.uri
    #>   <chr>                             <chr>         <int>          <int> <chr>
    #> 1 S6A_P4_2__LR______20250110T11091… S6A_…             0              5 https://…
    #> 2 xfdumanifest.xml                  xfdu…        126456              0 https://…
    #> 3 EOPMetadata.xml                   EOPM…         13800              0 https://…
    #> 4 S6A_P4_2__LR_RED__NT_153_196_202… S6A_…       1410175              0 https://…
    #> 5 S6A_P4_2__LR_STD__NT_153_196_202… S6A_…      10670647              0 https://…
    #> 6 manifest.xml                      mani…          1727              0 https://…

If available you can also get a quick peek at your product by calling `dse_odata_quicklook()`:

    dse_odata_quicklook(
        "91822f33-b15c-5b60-aa39-6d9f6f5c773b",
        tempfile(fileext = ".jpg"))

## Downloading Data

As shown in the ‘exploring’ section above, products can contain ‘nodes’, which could either represent a file or a directory. If you want to download all files in such a directory you can call `dse_odata_download_path()`. It will download all files in the specified node path. This is demonstrated below:

    if (dse_has_account()) {
      dse_odata_download_path(
        product     = "2f497806-0101-5eea-83fa-c8f68bc56b0c",
        node_path   =
          paste("DEM1_SAR_DTE_90_20101213T034716_20130408T035028_ADS_000000_5033.DEM",
                "Copernicus_DSM_30_S09_00_E026_00", "DEM",
                "Copernicus_DSM_30_S09_00_E026_00_DEM.dt1", sep = "/"),
        destination = tempdir()
        )
    }

The example above uses an alternative https route and requires a private access token, which in turn requires your Copernicus Data Space user id (usually your e-mail address) and password (see `vignette("Authentication")`).

The formal OData route uses [S3 buckets](https://en.wikipedia.org/wiki/Amazon_S3 and requires S3 authentication (see `vignette("Authentication")`). You can perform such downloads with `dse_odata_downloads()` as shown below

    if (dse_has_s3_secret()) {
      dse_odata_download(
          dse_odata_products(
            Name ==
              "S1A_IW_OCN__2SDH_20250707T210608_20250707T210625_059983_07739B_893E.SAFE"),
          destination = tempdir())
    }