Repository: CS-SI/eodag
Pull request #1703 (2025-06-24 15:39:14)

feat(core): import stac items as SearchResult
Import STAC items as `SearchResult` using new method `EODataAccessGateway.import_stac_items()`

github-actions[bot]: ## Test Results
    4 files  ±0      4 suites  ±0   5m 51s ⏱️ +3s
  644 tests ±0    641 ✅ ±0    3 💤 ±0  0 ❌ ±0 
2 576 runs  ±0  2 474 ✅ ±0  102 💤 ±0  0 ❌ ±0 

Results for commit ffb932b5. ± Comparison against base commit 920312e4.

[test-results]:data:application/gzip;base64,H4sIAG3HWmgC/03MQQ6DIBCF4asY1l0AzoDtZRpASEhVGoSV6d2LVqjL/5vM24jzk13Jo4NbR9bsU4sxR5V8WEr2yAqUU9qPAqDWc83GHMT+9PLv/aeBU34qQBvYGEM8JeZl3+QoxVl1k4OERr9NRnmVy+jR100T5tmnEsQ5fe+5RpSaCbQGzIADQ+WsU7JXQo4jRcqAfL4QmZdoCQEAAA==

eodag-bot: ![badge](https://raw.githubusercontent.com/CS-SI/eodag/_xml_coverage_reports/data/download-stac-items/./badge.svg)


## Code Coverage (Ubuntu)

<details>


```
Filename                                     Stmts    Miss  Cover    Missing
-----------------------------------------  -------  ------  -------  ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
__init__.py                                      8       0  100.00%
cli.py                                         323      64  80.19%   62-63, 87, 674-737, 839-890, 894
config.py                                      450      30  93.33%   81-83, 92, 100, 104-106, 177, 188, 205-206, 723-725, 849-852, 896-897, 906-907, 941, 1021, 1080-1085, 1087
crunch.py                                        5       5  0.00%    20-24
api/__init__.py                                  0       0  100.00%
api/core.py                                    820      91  88.90%   380-381, 431, 700, 731, 775-778, 816, 860, 894, 939-944, 970, 1061, 1129, 1352-1364, 1404, 1406, 1436, 1440-1451, 1464-1470, 1555-1558, 1595-1615, 1684-1689, 1701-1704, 1722-1734, 2019, 2043-2049, 2300, 2304-2308, 2317-2319, 2363-2364, 2393-2394, 2487-2503
api/search_result.py                           122      64  47.54%   87, 96, 103, 117, 232-331
api/product/__init__.py                          6       0  100.00%
api/product/_assets.py                          52       5  90.38%   97, 183, 191, 194-198
api/product/_product.py                        213      19  91.08%   72-74, 238-239, 345, 372, 473, 491, 515-518, 527-530, 585-588
api/product/metadata_mapping.py                708      63  91.10%   122-124, 222, 254-255, 312-324, 326, 337, 362, 402-403, 440, 461-464, 487, 495-496, 589-590, 614-615, 621-624, 639-640, 789, 840, 993, 1002-1006, 1023-1028, 1155, 1169-1189, 1209, 1214, 1366, 1380, 1405, 1451, 1512, 1535-1536, 1556-1560, 1576, 1584
api/product/drivers/__init__.py                 20       2  90.00%   33-36
api/product/drivers/base.py                     29       1  96.55%   98
api/product/drivers/generic.py                   7       0  100.00%
api/product/drivers/sentinel1.py                15       0  100.00%
api/product/drivers/sentinel2.py                15       0  100.00%
plugins/__init__.py                              0       0  100.00%
plugins/base.py                                 21       2  90.48%   48, 55
plugins/manager.py                             173      19  89.02%   105-110, 145-150, 154, 192, 214, 218, 242, 281-282, 382-385, 397-398
plugins/apis/__init__.py                         0       0  100.00%
plugins/apis/base.py                             4       0  100.00%
plugins/apis/ecmwf.py                           97      10  89.69%   165-167, 214-215, 241-243, 296-297
plugins/apis/usgs.py                           180      26  85.56%   156, 258, 292, 327-329, 334, 360-361, 366, 396-403, 414-419, 441-447, 478
plugins/authentication/__init__.py               6       1  83.33%   31
plugins/authentication/aws_auth.py              20       0  100.00%
plugins/authentication/base.py                  17       2  88.24%   45, 58
plugins/authentication/generic.py               16       3  81.25%   50, 55, 65
plugins/authentication/header.py                19       0  100.00%
plugins/authentication/keycloak.py              46       7  84.78%   153-156, 177-182
plugins/authentication/oauth.py                 13       7  46.15%   43-45, 49-52
plugins/authentication/openid_connect.py       231      28  87.88%   92-93, 105-123, 170, 176-204, 212, 351-354, 380, 421
plugins/authentication/qsauth.py                34       1  97.06%   91
plugins/authentication/sas_auth.py              47       1  97.87%   76
plugins/authentication/token.py                128       9  92.97%   180, 217, 289-290, 340-344
plugins/authentication/token_exchange.py        36      14  61.11%   75, 93-121
plugins/crunch/__init__.py                       0       0  100.00%
plugins/crunch/base.py                          10       1  90.00%   43
plugins/crunch/filter_date.py                   59      14  76.27%   52-57, 69, 78, 87, 90, 100-102, 109-111, 118
plugins/crunch/filter_latest_intersect.py       47       8  82.98%   52-53, 69, 78-81, 83, 90-93
plugins/crunch/filter_latest_tpl_name.py        31       1  96.77%   83
plugins/crunch/filter_overlap.py                66      18  72.73%   28-30, 66-69, 76-79, 85, 93, 104-120
plugins/crunch/filter_property.py               30       5  83.33%   55-60, 63-64
plugins/download/__init__.py                     0       0  100.00%
plugins/download/aws.py                        528     158  70.08%   272, 285, 365-368, 382-386, 476-478, 482, 515-516, 522-526, 556, 589, 593, 600, 630-638, 642, 677-685, 696-698, 729-810, 828-886, 897-902, 955, 970-972, 975, 985-993, 1001-1015, 1025-1048, 1055-1068, 1106, 1132, 1177-1179, 1399
plugins/download/base.py                       256      42  83.59%   128, 156, 236-239, 292-293, 341-345, 351-352, 394, 397-411, 423, 427, 491-495, 525-526, 551-559, 561-565, 615, 636, 658, 666
plugins/download/creodias_s3.py                 25       9  64.00%   55-69
plugins/download/http.py                       542      93  82.84%   228, 270-273, 334-337, 340, 347-352, 383-385, 402, 417, 471, 506, 520, 534, 542-546, 562-567, 578, 596, 632-635, 656, 666, 673, 693, 770, 789-800, 808-813, 823-840, 858, 888-897, 933, 958-959, 978-983, 992, 1007-1009, 1013, 1016, 1031-1032, 1067, 1129, 1144, 1203-1204, 1210, 1220, 1256, 1292, 1312, 1347-1349
plugins/download/s3rest.py                     120      26  78.33%   119, 153, 160, 195, 222-229, 232-234, 238, 249-255, 263-264, 267-271, 286-289, 298, 319-322
plugins/search/__init__.py                      22       0  100.00%
plugins/search/base.py                         151      11  92.72%   101, 105, 129-135, 276, 297, 436
plugins/search/build_search_result.py          477      56  88.26%   245-246, 282, 285, 310, 313, 344-346, 554, 556, 560, 563, 690, 692, 753, 760, 784, 821, 872, 922-937, 985, 1010, 1013, 1017, 1138-1139, 1148-1157, 1220, 1241, 1260-1269, 1386-1387, 1432, 1441-1443, 1503, 1549
plugins/search/cop_marine.py                   252      47  81.35%   56, 64-66, 76-77, 82, 87-88, 104, 106, 109, 175-176, 232, 238, 242, 246, 259, 270-271, 279, 310, 314, 335, 339, 343, 347, 351-355, 361-364, 367-381, 398, 447-451, 456, 468
plugins/search/creodias_s3.py                   25       1  96.00%   51
plugins/search/csw.py                          105      81  22.86%   98-99, 103-104, 112-160, 166-179, 187-219, 237-278
plugins/search/data_request_search.py          207      72  65.22%   190-193, 209, 220, 224-225, 236, 241, 246, 253, 266-269, 323-324, 328, 338-344, 349, 367-371, 387-390, 398-409, 426, 428, 435-438, 440-441, 459-463, 496, 506, 517, 530, 536-551, 556
plugins/search/qssearch.py                     710      88  87.61%   448, 506, 520, 524-530, 545-546, 558-562, 675-687, 732-735, 806-807, 858, 877, 884, 896, 953, 974, 977-978, 987-988, 997-998, 1007-1008, 1035, 1119-1124, 1128-1137, 1171, 1191, 1264-1265, 1271, 1361, 1522, 1525, 1531-1532, 1553, 1581-1593, 1600, 1632-1634, 1644-1650, 1680, 1703, 1708-1709, 1724, 1740, 1827, 1834, 1845, 1868-1873, 1882, 1891
plugins/search/stac_list_assets.py              25      10  60.00%   44-51, 75-85
plugins/search/static_stac_search.py            79      13  83.54%   100-126, 170, 183
rest/__init__.py                                 4       2  50.00%   21-22
rest/cache.py                                   33       7  78.79%   35-37, 53-55, 59, 68
rest/config.py                                  25       0  100.00%
rest/constants.py                                6       0  100.00%
rest/core.py                                   260      62  76.15%   258, 301, 316-352, 448, 485-524, 601, 631-634, 707, 714-767
rest/errors.py                                  72       5  93.06%   117, 127, 138, 163-164
rest/server.py                                 192      24  87.50%   86, 109-111, 285-290, 318, 516-518, 535-540, 569, 571, 575-576, 580-581
rest/stac.py                                   333      72  78.38%   312, 334, 386-389, 416-443, 474-476, 499, 531-532, 614-654, 676-692, 784-788, 795, 850-851, 861-866, 876-880, 929, 1019-1021
rest/types/__init__.py                           0       0  100.00%
rest/types/collections_search.py                13      13  0.00%    18-44
rest/types/eodag_search.py                     180       6  96.67%   225-229, 282, 285, 353, 375
rest/types/queryables.py                        64      12  81.25%   61, 71-79, 105-110, 174
rest/types/stac_search.py                      125       7  94.40%   128, 174, 189-191, 199, 203
rest/utils/__init__.py                          94      12  87.23%   101-102, 121-123, 175, 185-199
rest/utils/cql_evaluate.py                      48       5  89.58%   69, 76, 90, 97, 105
rest/utils/rfc3339.py                           23       3  86.96%   48, 60, 62
types/__init__.py                              161      40  75.16%   67, 80-84, 95-107, 135-137, 144-149, 252, 262-278, 283, 285, 307, 312, 320, 330
types/bbox.py                                   43      19  55.81%   46-61, 72-74, 85-87, 99-101, 113-115, 123
types/download_args.py                           9       0  100.00%
types/queryables.py                            111       0  100.00%
types/search_args.py                            70      18  74.29%   60-64, 71-88, 103
types/whoosh.py                                 81      16  80.25%   129-132, 136-143, 155-161, 174-176
utils/__init__.py                              551      40  92.74%   81, 208-209, 218-245, 248, 263, 343-347, 420-424, 443-445, 459, 551, 566, 606-607, 636, 1011-1014, 1022-1023, 1061-1062, 1109-1110, 1250
utils/cache.py                                  22       0  100.00%
utils/env.py                                     3       0  100.00%
utils/exceptions.py                             46       0  100.00%
utils/import_system.py                          28      19  32.14%   64-78, 89-99
utils/logging.py                                28       1  96.43%   41
utils/notebook.py                               44      23  47.73%   25-29, 36-41, 58-62, 72-78, 83-87
utils/repr.py                                   38       8  78.95%   51, 53, 57, 98, 122-129
utils/requests.py                               55      29  47.27%   51-52, 64, 85-96, 107-124, 128
utils/rest.py                                   36       1  97.22%   55
utils/s3.py                                     69       3  95.65%   162-163, 226
utils/stac_reader.py                           113      44  61.06%   63-85, 95-97, 101, 138, 154-159, 206-216, 226-256
TOTAL                                        10628    1719  83.83%
```

</details>


### Diff against develop

```
Filename                Stmts    Miss  Cover
--------------------  -------  ------  -------
api/core.py               +12     +11  -1.20%
api/search_result.py      +66     +60  -45.32%
utils/__init__.py          +2       0  +0.03%
TOTAL                     +80     +71  -0.55%
```


Results for commit: ffb932b557b165ec4c85815afefa73a67dd05014


_Minimum allowed coverage is `70%`_


:recycle: This comment has been updated with latest results


<!-- Sticky Pull Request Commentartifacts/unit-test-results-python3.13-ubuntu-latest/coverage.xml -->

Patch:
diff --git a/README.rst b/README.rst
index a1454df6..a08f2388 100644
--- a/README.rst
+++ b/README.rst
@@ -59,7 +59,7 @@ integrate new data providers. Three types of plugins compose the tool:
 
 * Authentication plugins, which are used to authenticate the user on the external services used (JSON Token, Basic Auth, OAUTH, ...).
 
-Since v2.0 EODAG can be run as `STAC client or server <https://eodag.readthedocs.io/en/latest/stac.html>`_.
+EODAG can be run as STAC client or server <https://eodag.readthedocs.io/en/latest/stac.html>`_.
 
 Read `the documentation <https://eodag.readthedocs.io/en/latest/>`_ for more insights.
 
diff --git a/docs/_static/product_types_information.csv b/docs/_static/product_types_information.csv
index 039a9de6..337ac0a8 100644
--- a/docs/_static/product_types_information.csv
+++ b/docs/_static/product_types_information.csv
@@ -40,11 +40,16 @@ ERA5_PL,"ERA5 is the fifth generation ECMWF reanalysis for the global climate an
 ERA5_PL_MONTHLY,"ERA5 is the fifth generation ECMWF reanalysis for the global climate and weather for the past 8 decades. Data is available from 1940 onwards. ERA5 replaces the ERA-Interim reanalysis. Reanalysis combines model data with observations from across the world into a globally complete and consistent dataset using the laws of physics. This principle, called data assimilation, is based on the method used by numerical weather prediction centres, where every so many hours (12 hours at ECMWF) a previous forecast is combined with newly available observations in an optimal way to produce a new best estimate of the state of the atmosphere, called analysis, from which an updated, improved forecast is issued. Reanalysis works in the same way, but at reduced resolution to allow for the provision of a dataset spanning back several decades. Reanalysis does not have the constraint of issuing timely forecasts, so there is more time to collect observations, and when going further back in time, to allow for the ingestion of improved versions of the original observations, which all benefit the quality of the reanalysis product. ERA5 provides hourly estimates for a large number of atmospheric, ocean-wave and land-surface quantities. An uncertainty estimate is sampled by an underlying 10-member ensemble at three-hourly intervals. Ensemble mean and spread have been pre-computed for convenience. Such uncertainty estimates are closely related to the information content of the available observing system which has evolved considerably over time. They also indicate flow-dependent sensitive areas. To facilitate many climate applications, monthly-mean averages have been pre-calculated too, though monthly means are not available for the ensemble mean and spread. ERA5 is updated daily with a latency of about 5 days (monthly means are available around the 6th of each month). In case that serious flaws are detected in this early release (called ERA5T), this data could be different from the final release 2 to 3 months later. So far this has only been the case for the month September 2021, while it will also be the case for October, November and December 2021. For months prior to September 2021 the final release has always been equal to ERA5T, and the goal is to align the two again after December 2021. ERA5 is updated daily with a latency of about 5 days (monthly means are available around the 6th of each month). In case that serious flaws are detected in this early release (called ERA5T), this data could be different from the final release 2 to 3 months later. In case that this occurs users are notified. The data set presented here is a regridded subset of the full ERA5 data set on native resolution. It is online on spinning disk, which should ensure fast and easy access. It should satisfy the requirements for most common applications. Data has been regridded to a regular lat-lon grid of 0.25 degrees for the reanalysis and 0.5 degrees for the uncertainty estimate (0.5 and 1 degree respectively for ocean waves). There are four main sub sets: hourly and monthly products, both on pressure levels (upper air fields) and single levels (atmospheric, ocean-wave and land surface quantities). ",,ERA5,ERA5,,"Climate,ECMWF,Reanalysis,ERA5,CDS,Atmospheric,land,sea,monthly,pressure,levels",ATMOSPHERIC,other,ERA5 monthly averaged data on pressure levels from 1940 to present,1940-01-01T00:00:00Z,ERA5_PL_MONTHLY,,,available,,,,,,available,,,,,,,,,,,,,,,,,,available,
 ERA5_SL,"ERA5 is the fifth generation ECMWF reanalysis for the global climate and weather for the past 8 decades. Data is available from 1940 onwards. ERA5 replaces the ERA-Interim reanalysis. Reanalysis combines model data with observations from across the world into a globally complete and consistent dataset using the laws of physics. This principle, called data assimilation, is based on the method used by numerical weather prediction centres, where every so many hours (12 hours at ECMWF) a previous forecast is combined with newly available observations in an optimal way to produce a new best estimate of the state of the atmosphere, called analysis, from which an updated, improved forecast is issued. Reanalysis works in the same way, but at reduced resolution to allow for the provision of a dataset spanning back several decades. Reanalysis does not have the constraint of issuing timely forecasts, so there is more time to collect observations, and when going further back in time, to allow for the ingestion of improved versions of the original observations, which all benefit the quality of the reanalysis product. ERA5 provides hourly estimates for a large number of atmospheric, ocean-wave and land-surface quantities. An uncertainty estimate is sampled by an underlying 10-member ensemble at three-hourly intervals. Ensemble mean and spread have been pre-computed for convenience. Such uncertainty estimates are closely related to the information content of the available observing system which has evolved considerably over time. They also indicate flow-dependent sensitive areas. To facilitate many climate applications, monthly-mean averages have been pre-calculated too, though monthly means are not available for the ensemble mean and spread. ERA5 is updated daily with a latency of about 5 days. In case that serious flaws are detected in this early release (called ERA5T), this data could be different from the final release 2 to 3 months later. In case that this occurs users are notified. The data set presented here is a regridded subset of the full ERA5 data set on native resolution. It is online on spinning disk, which should ensure fast and easy access. It should satisfy the requirements for most common applications. Data has been regridded to a regular lat-lon grid of 0.25 degrees for the reanalysis and 0.5 degrees for the uncertainty estimate (0.5 and 1 degree respectively for ocean waves). There are four main sub sets: hourly and monthly products, both on pressure levels (upper air fields) and single levels (atmospheric,ocean-wave and land surface quantities). ",,ERA5,ERA5,,"ECMWF,Reanalysis,ERA5,CDS,Atmospheric,land,sea,hourly,single,levels",ATMOSPHERIC,other,ERA5 hourly data on single levels from 1940 to present,1940-01-01T09:00:00Z,ERA5_SL,,,available,,,,,,available,,,,,,,,,,,,,,,,,,available,
 ERA5_SL_MONTHLY,"ERA5 is the fifth generation ECMWF reanalysis for the global climate and weather for the past 8 decades. Data is available from 1940 onwards. ERA5 replaces the ERA-Interim reanalysis. Reanalysis combines model data with observations from across the world into a globally complete and consistent dataset using the laws of physics. This principle, called data assimilation, is based on the method used by numerical weather prediction centres, where every so many hours (12 hours at ECMWF) a previous forecast is combined with newly available observations in an optimal way to produce a new best estimate of the state of the atmosphere, called analysis, from which an updated, improved forecast is issued. Reanalysis works in the same way, but at reduced resolution to allow for the provision of a dataset spanning back several decades. Reanalysis does not have the constraint of issuing timely forecasts, so there is more time to collect observations, and when going further back in time, to allow for the ingestion of improved versions of the original observations, which all benefit the quality of the reanalysis product. ERA5 provides hourly estimates for a large number of atmospheric, ocean-wave and land-surface quantities. An uncertainty estimate is sampled by an underlying 10-member ensemble at three-hourly intervals. Ensemble mean and spread have been pre-computed for convenience. Such uncertainty estimates are closely related to the information content of the available observing system which has evolved considerably over time. They also indicate flow-dependent sensitive areas. To facilitate many climate applications, monthly-mean averages have been pre-calculated too, though monthly means are not available for the ensemble mean and spread. ERA5 is updated daily with a latency of about 5 days (monthly means are available around the 6th of each month). In case that serious flaws are detected in this early release (called ERA5T), this data could be different from the final release 2 to 3 months later. In case that this occurs users are notified. The data set presented here is a regridded subset of the full ERA5 data set on native resolution. It is online on spinning disk, which should ensure fast and easy access. It should satisfy the requirements for most common applications. Data has been regridded to a regular lat-lon grid of 0.25 degrees for the reanalysis and 0.5 degrees for the uncertainty estimate (0.5 and 1 degree respectively for ocean waves). There are four main sub sets: hourly and monthly products, both on pressure levels (upper air fields) and single levels (atmospheric, ocean-wave and land surface quantities). ",,ERA5,ERA5,,"Climate,ECMWF,Reanalysis,ERA5,CDS,Atmospheric,land,sea,monthly,single,levels",ATMOSPHERIC,other,ERA5 monthly averaged data on single levels from 1940 to present,1940-01-01T00:00:00Z,ERA5_SL_MONTHLY,,,available,,,,,,available,,,,,,,,,,,,,,,,,,available,
+EUSTAT_AVAILABLE_BEDS_HOSPITALS_NUTS2,"Non-expenditure healthcare data provide information on institutions providing healthcare in countries, on resources used and on output produced in the framework of healthcare provision. \nData on healthcare form a major element of public health information as they describe the capacities available for different types of healthcare provision as well as potential 'bottlenecks' observed. The quantity and quality of healthcare services provided and the work sharing established between the different institutions are a subject of ongoing debate in all countries. Sustainability - continuously providing the necessary monetary and personal resources needed - and meeting the challenges of ageing societies are the primary perspectives used when analysing and using the data. \nThe resource-related data refer to both human and technical resources, i.e. they relate to: \n- Health care staff: 'manpower' active in the health care sector (doctors, dentists, nurses, etc.);\n- Heath workforce migration: migration movements of doctors and nurses;\n- Healthcare facilities: technical capacity dimensions (hospital beds, beds in nursing and residential care facilities, etc.).\nThe output-related data ('activities') refer to contacts between patients and the healthcare system, and to the treatment thereby received. Data are available for hospital discharges of in-patients and day cases, average length of stay of in-patients, consultations with medical professionals, and medical procedures performed in hospitals.\nAnnual national and regional data are provided in absolute numbers, percentages, and in population-standardised rates (per 100 000 inhabitants).\nWherever applicable, the definitions and classifications of the System of Health Accounts (SHA) are followed, e.g. International Classification for Health Accounts - Providers of health care (ICHA-HP). For hospital discharges, the International Shortlist for Hospital Morbidity Tabulation (ISHMT) is used. Surgical procedures are classified according to a shortlist mapped to ICD-9-CM.\nThese healthcare data are largely based on administrative data sources in the countries. Therefore, they reflect the country-specific way of organising healthcare and may not always be completely comparable. ",,Eurostat,Eurostat,,"Eurostat, Health care, Hospital, Bed, Health",,proprietary,Available beds in hospitals by NUTS 2 region,2013-01-01T00:00:00Z,EUSTAT_AVAILABLE_BEDS_HOSPITALS_NUTS2,,,,,,,,,available,,,,,,,,,,,,,,,,,,,
+EUSTAT_BATHING_SITES_WATER_QUALITY,"The indicator measures the number and proportion of coastal and inland bathing sites with excellent water quality. The indicator assessment is based on microbiological parameters (intestinal enterococci and Escherichia coli). The new Bathing Water Directive requires Member States to identify and assess the quality of all inland and marine bathing waters and to classify these waters as ‘poor’, ‘sufficient’, ‘good’ or ‘excellent’. ",,Eurostat,Eurostat,,"Eurostat, Bath, Water, Water quality",,proprietary,Bathing sites with excellent water quality by location,2011-01-01T00:00:00Z,EUSTAT_BATHING_SITES_WATER_QUALITY,,,,,,,,,available,,,,,,,,,,,,,,,,,,,
 EUSTAT_GREENHOUSE_GAS_EMISSION_AGRICULTURE,"This indicator tracks trends in greenhouse gas (GHG) emissions by agriculture, estimated and reported under the United Nations Framework Convention on Climate Change (UNFCCC), the Kyoto Protocol and the Decision 525/2013/EC. The annual data collection covers in principle all Member States of the European Union as well as some other European countries ",,Eurostat,Eurostat,,"Eurostat, Agriculture, Greenhouse gas, CO2, Emission, Air pollutants",,proprietary,Eurostat - Greenhouse gas emissions from agriculture,2011-01-01T00:00:00Z,EUSTAT_GREENHOUSE_GAS_EMISSION_AGRICULTURE,,,,,,,,,available,,,,,,,,,,,,,,,,,,,
 EUSTAT_POP_AGE_GROUP_SEX_NUTS3,"Each year Eurostat collects demographic data at regional level from 37 countries as part of the Unified Demography (Unidemo) project. UNIDEMO is Eurostat's main annual demographic data collection and aims to gather information on demography and migration. This dataset contains information about the population by sex, age and region of residence (NUTS 3 level). ",,Eurostat,Eurostat,,"Eurostat, Population, Age, Sex, NUTS 3, Unidemo, Demographic",,proprietary,"Population on 1 January by age, sex and NUTS 3 region",2014-01-01T00:00:00Z,EUSTAT_POP_AGE_GROUP_SEX_NUTS3,,,,,,,,,available,,,,,,,,,,,,,,,,,,,
 EUSTAT_POP_AGE_SEX_NUTS2,"Each year Eurostat collects demographic data at regional level from 37 countries as part of the Unified Demography (Unidemo) project. UNIDEMO is Eurostat's main annual demographic data collection and aims to gather information on demography and migration. This dataset contains information about the population by sex, age and region of residence (NUTS 2 level). ",,Eurostat,Eurostat,,"Eurostat, Population, Age, Sex, NUTS 2, Unidemo, Demographic",,proprietary,"Population on 1 January by age, sex and NUTS 2 region",1990-01-01T00:00:00Z,EUSTAT_POP_AGE_SEX_NUTS2,,,,,,,,,available,,,,,,,,,,,,,,,,,,,
 EUSTAT_POP_CHANGE_DEMO_BALANCE_CRUDE_RATES_NUTS3,Each year Eurostat collects demographic data at regional level from 37 countries as part of the Unified Demography (Unidemo) project. UNIDEMO is Eurostat's main annual demographic data collection and aims to gather information on demography and migration. This dataset contains information about demographic balance and crude rates of a population at regional level (NUTS 3 level). ,,Eurostat,Eurostat,,"Eurostat, Population, Age, Sex, NUTS 3, Unidemo, Demographic",,proprietary,Population change - Demographic balance and crude rates at regional level (NUTS 3),2000-01-01T00:00:00Z,EUSTAT_POP_CHANGE_DEMO_BALANCE_CRUDE_RATES_NUTS3,,,,,,,,,available,,,,,,,,,,,,,,,,,,,
+EUSTAT_POP_DENSITY_NUTS3,"Eurostat’s annual data collections on population. Member States send population data to Eurostat data as on of 31 December for the reference year under Regulation 1260/2013 on European demographic statistics. The data are conventionally published by Eurostat as population on 1 January of the following year (reference year + 1). \nThe aim is to collect annual mandatory and voluntary demographic data from the national statistical institutes. Mandatory data are those defined by the legislation listed under ‘6.1. Institutional mandate — legal acts and other agreements’. \nThe completeness of the demographic data collected on a voluntary basis depends on the availability and completeness of information provided by the national statistical institutes.\nFor more information on mandatory/voluntary data collection, see 6.1. Institutional mandate — legal acts and other agreements. \nThe following statistics are available. \nPopulation on 1 January by sex and by:\n- single age and educational attainment / marital status / broad group of citizenship / broad group of country of birth;\n  - five-year age group and citizenship / country of birth;\n  - citizenship and broad group of country of birth / country of birth and broad group of citizenship;\n  - broad age group and NUTS 3 (under regional data population folder);\n  - single age and NUTS 2 (under regional data population folder);\n  - five-year age group and NUTS 2 / NUTS 3 (under regional data population folder).\nPopulation structure statistics: median age of population, proportion of population by various age groups, old age dependency ratio. ",,Eurostat,Eurostat,,"Eurostat, Population, Density, NUTS 3",,proprietary,Population density by NUTS 3 region,1990-01-01T00:00:00Z,EUSTAT_POP_DENSITY_NUTS3,,,,,,,,,available,,,,,,,,,,,,,,,,,,,
 EUSTAT_SHARE_ENERGY_FROM_RENEWABLE,"This dataset covers the indicator for monitoring progress towards renewable energy targets of the Europe 2020 strategy implemented by Directive 2009/28/EC on the promotion of the use of energy from renewable sources. The annual data collection covers in principle all Member States of the European Union. Time series starts in the year 2004. Due to the change of legal basis, a break in series occurs between 2020 and 2021. The calculation is based on data collected in the framework of Regulation (EC) No 1099/2008 on energy statistics and complemented by specific supplementary data transmitted by national administrations to Eurostat. In some countries the statistical systems are not yet fully developed to meet all requirements of Directive 2009/28/EC, in particular with respect to ambient heat captured from the environment by heat pumps renewable cooling or sustainability of solid and gaseous biofuels. This is indicator is a Sustainable Development Goal (SDG). It has been chosen for the assessment of the progress towards the objectives and targets of the EU Sustainable Development Strategy. The data collection covers the full spectrum of the Member States of the European Union.The share of energy from renewable sources is calculated for four indicators: Transport (RES-T), Heating and Cooling (RES-H&C), Electricity (RES-E), Overall RES share (RES) ",,Eurostat,Eurostat,,"Eurostat, Energy, Renewable, Transport, Heating, Cooling, Electricity",,proprietary,Share of energy from renewable sources,2004-01-01T00:00:00Z,EUSTAT_SHARE_ENERGY_FROM_RENEWABLE,,,,,,,,,available,,,,,,,,,,,,,,,,,,,
+EUSTAT_SOIL_SEALING_INDEX,"The indicator estimates the increase in sealed soil surfaces with impervious materials due to urban development and construction (e.g. buildings, constructions and laying of completely or partially impermeable artificial material, such as asphalt, metal, glass, plastic or concrete). This provides an indication of the rate of soil sealing, when areas change land use towards artificial and urban land use. The indicator builds on data from the imperviousness High Resolution Layer (a product of the Copernicus Land Monitoring Service). The indicator is presented in the following units: Index 2006=100 % of total surface total sealed surface in km2. ",,Eurostat,Eurostat,,"Eurostat, soil, soil sealing, SDG, EU Sustainable Development Goals",,proprietary,Soil sealing index,2006-01-01T00:00:00Z,EUSTAT_SOIL_SEALING_INDEX,,,,,,,,,available,,,,,,,,,,,,,,,,,,,
+EUSTAT_SURFACE_TERRESTRIAL_PROTECTED_AREAS,The indicator measures the surface of terrestrial protected areas. The indicator comprises nationally designated protected areas and Natura 2000 sites. A nationally designated area is an area protected by national legislation. The Natura 2000 network comprises both marine and terrestrial protected areas designated under the EU Habitats and Birds Directives with the goal to maintain or restore a favourable conservation status for habitat types and species of EU interest. ,,Eurostat,Eurostat,,"Eurostat, CO2, terrestrial, protected areas",,proprietary,Surface of the terrestrial protected areas,2013-01-01T00:00:00Z,EUSTAT_SURFACE_TERRESTRIAL_PROTECTED_AREAS,,,,,,,,,available,,,,,,,,,,,,,,,,,,,
 FIRE_HISTORICAL,"This data set provides complete historical reconstruction of meteorological conditions favourable to the start, spread and sustainability of fires. The fire danger metrics provided are part of a vast dataset produced by the Copernicus Emergency Management Service for the European Forest Fire Information System (EFFIS). The European Forest Fire Information System incorporates the fire danger indices for three different models developed in Canada, United States and Australia. In this dataset the fire danger indices are calculated using weather forecast from historical simulations provided by ECMWF ERA5 reanalysis. ERA5 by combining model data and a vast set of quality controlled observations provides a globally complete and consistent data-set and is regarded as a good proxy for observed atmospheric conditions. The selected data records in this data set are regularly extended with time as ERA5 forcing data become available. This dataset is produced by ECMWF in its role of the computational centre for fire danger forecast of the CEMS, on behalf of the Joint Research Centre which is the managing entity of the service. Variables in the dataset/application are: Build-up index, Burning index, Danger rating, Drought code, Duff moisture code, Energy release component, Fine fuel moisture code, Fire daily severity index, Fire danger index, Fire weather index, Ignition component, Initial spread index, Keetch-Byram drought index, Spread component ",,CEMS,CEMS,,"ECMWF,EFFIS,fire,historical,ERA5,european,sustainability,CEMS,system",ATMOSPHERIC,other,Fire danger indices historical data from the Copernicus Emergency Management Service,1940-03-01T00:00:00Z,FIRE_HISTORICAL,,,,,available,,,,available,,,,,,,,,,,,,,,,,,available,
 FIRE_SEASONAL,"This dataset offers modeled daily fire danger time series, driven by seasonal weather forecasts. It provides long-range predictions of meteorological conditions conducive to the initiation, spread, and persistence of fires. The fire danger metrics included in this dataset are part of an extensive dataset produced by the Copernicus Emergency Management Service (CEMS) for the European Forest Fire Information System (EFFIS) and the Global Wildfire Information System (GWIS). EFFIS and GWIS are used for monitoring and forecasting fire danger at both European and global scales. The dataset incorporates fire danger indices from the U.S. Forest Service National Fire-Danger Rating System (NFDRS), the Canadian Forest Service Fire Weather Index Rating System (FWI), and the Australian McArthur (Mark 5) rating systems. This dataset was generated by driving the Global ECMWF Fire Forecast (GEFF) model with seasonal weather ensemble forecasts from the European Centre for Medium-Range Weather Forecasts (ECMWF) System 5 (SEAS5) prediction system.These forecasts initially consist of 25 ensemble members until December 2016, referred to as re-forecasts. After that period, they consist of seasonal forecasts with 51 members. It is important to note that the re-forecast dataset was initialized using ERA-Interim analysis data, while forecast simulations from 2016 onward are initialized using ECMWF operational analysis. Therefore, it is suggested that the period 1981-2016 be used as a reference period, while the period 2017-to present as a real time forecast. For both the re-forecast (1981-2016) and forecast periods (2017-present), the temporal resolution is daily forecasts at 12:00 local time, available once a month, with a prediction horizon of 216 days (equivalent to 7 months). The data records in this dataset will be extended over time as seasonal forcing data becomes available. Once the SEAS5 operation ceases, the dataset will be updated with the next ECMWF seasonal system (SEAS6). It is essential to note that this is not a real-time service, as real-time forecasts are accessible through the EFFIS web services. These seasonal forecasts can be used to assess the performance of the forecasting system or to develop tools for statistically correcting forecast errors. ECMWF produces this dataset as the computational center for fire danger forecasting within the Copernicus Emergency Management Service (CEMS) on behalf of the Joint Research Centre, which serves as the managing entity for this service. ",,CEMS,CEMS,,"ECMWF,CEMS,EFFIS,GWIS,fire,danger,seasonal,GEFF",,other,Seasonal forecast of fire danger indices from the Copernicus Emergency Management Service,1981-02-01T00:00:00Z,FIRE_SEASONAL,,,,,available,,,,,,,,,,,,,,,,,,,,,,,
 GLACIERS_DIST_RANDOLPH,"A glacier is defined as a perennial mass of ice, and possibly firn and snow, originating on the land surface from the recrystallization of snow or other forms of solid precipitation and showing evidence of past or present flow. There are several types of glaciers such as glacierets, mountain glaciers, valley glaciers and ice fields, as well as ice caps. Some glacier tongues reach into lakes or the sea, and can develop floating ice tongues or ice shelves. Glacier changes are recognized as independent and high-confidence natural indicators of climate change. Past, current and future glacier changes affect global sea level, the regional water cycle and local hazards.\nThis dataset is a snapshot of global glacier outlines compiled from\nmaps, aerial photographs and satellite images mostly acquired in the period 2000-2010. ",,,INSITU,,"ECMWF,WGMS,INSITU,CDS,C3S,glacier,randolph,distribution,inventory",ATMOSPHERIC,other,Glaciers distribution data from the Randolph Glacier Inventory for year 2000,2000-01-01T00:00:00Z,GLACIERS_DIST_RANDOLPH,,,available,,,,,,available,,,,,,,,,,,,,,,,,,available,
diff --git a/docs/api_reference/core.rst b/docs/api_reference/core.rst
index 1bcf0a36..1d40f614 100644
--- a/docs/api_reference/core.rst
+++ b/docs/api_reference/core.rst
@@ -76,9 +76,11 @@ Misc
    EODataAccessGateway.guess_product_type
    EODataAccessGateway.list_queryables
    EODataAccessGateway.available_sortables
+   EODataAccessGateway.import_stac_items
 
 .. autoclass:: eodag.api.core.EODataAccessGateway
    :members: add_provider, set_preferred_provider, get_preferred_provider, update_providers_config, list_product_types,
              available_providers, search, search_all, search_iter_page, crunch, download, download_all, serialize,
              deserialize, deserialize_and_register, load_stac_items, group_by_extent, guess_product_type, get_cruncher,
-             update_product_types_list, fetch_product_types_list, discover_product_types, list_queryables, available_sortables
+             update_product_types_list, fetch_product_types_list, discover_product_types, list_queryables,
+             available_sortables, import_stac_items
diff --git a/docs/api_user_guide.rst b/docs/api_user_guide.rst
index 73f61fb0..3ab50934 100644
--- a/docs/api_user_guide.rst
+++ b/docs/api_user_guide.rst
@@ -30,3 +30,4 @@ or run locally after being downloaded (see how to :ref:`install_notebooks`).
    notebooks/api_user_guide/7_crunch.ipynb
    notebooks/api_user_guide/8_download.ipynb
    notebooks/api_user_guide/9_post_process.ipynb
+   notebooks/tutos/tuto_stac_client.ipynb
diff --git a/docs/cli_user_guide.rst b/docs/cli_user_guide.rst
index 8575a13d..74c39a5f 100644
--- a/docs/cli_user_guide.rst
+++ b/docs/cli_user_guide.rst
@@ -9,52 +9,11 @@ Then you can start playing with it:
 
 * Run ``eodag --help`` to display all the available options and commands:
 
-.. code-block:: console
-
-        Usage: eodag [OPTIONS] COMMAND [ARGS]...
-
-        Earth Observation Data Access Gateway: work on EO products from any
-        provider
-
-        Options:
-        -v, --verbose  Control the verbosity of the logs. For maximum verbosity,
-                        type -vvv
-        --help         Show this message and exit.
-
-        Commands:
-        deploy-wsgi-app  Configure the settings of the HTTP web app (the...
-        discover         Fetch providers to discover product types
-        download         Download a list of products from a serialized search...
-        list             List supported product types
-        search           Search satellite images by their product types,...
-        serve-rest       Start eodag HTTP server
-        version          Print eodag version and exit
-
-* Each command has its own help, see for instance the help of the ``list`` command with ``eodag list --help``:
+.. command-output:: eodag --help
 
-.. code-block:: console
+* Each command has its own help, see for instance the help of the ``list`` command with ``eodag list --help``.
 
-        Usage: eodag list [OPTIONS]
-
-        List supported product types
-
-        Options:
-        -p, --provider TEXT             List product types supported by this
-                                        provider
-        -i, --instrument TEXT           List product types originating from this
-                                        instrument
-        -P, --platform TEXT             List product types originating from this
-                                        platform
-        -t, --platformSerialIdentifier TEXT
-                                        List product types originating from the
-                                        satellite identified by this keyword
-        -L, --processingLevel TEXT      List product types of processing level
-        -S, --sensorType TEXT           List product types originating from this
-                                        type of sensor
-        --no-fetch                      Do not fetch providers for new product types
-        --help                          Show this message and exit.
-
-* By default the command line interface of eodag is set to the minimum verbosity level. You can print more
+* By default the command line interface of eodag is set to the minimum **verbosity level**. You can print more
   log messages by adding ``-v`` to eodag master command. The more ``v`` given (up to 3), the more verbose the tool is.
   This feature comes in handy when you want to inspect an error or an unexpected behaviour. 4 different verbosity levels
   are offered to you:
@@ -66,6 +25,11 @@ Then you can start playing with it:
         eodag -vv list
         eodag -vvv list
 
+Search
+------
+
+.. command-output:: eodag search --help
+
 * To search for products and crunch the results of the search:
 
 .. code-block:: console
@@ -105,17 +69,19 @@ string search sent to the provider. For instance, if you want to add foo=1 and b
                      --cruncher-args FilterOverlap minimum_overlap 10 \
                      --query "foo=1&bar=2"
 
-* If the product type is not known, it can also be guessed by EODAG during the search based on parameters in the search request. The possible parameters are:
+* If the product type is not known, it can also be guessed by EODAG during the search based on parameters in the search
+  request. The possible parameters are:
 
-        * `instrument` (e.g. MSI)
-        * `platform` (e.g. SENTINEL2)
-        * `platformSerialIdentifier` (e.g. S2A)
-        * `processingLevel` (e.g. L1)
-        * `sensorType` (e.g. OPTICAL)
-        * `keywords` (e.g. SENTINEL2 L1C SAFE), which is case insensitive and ignores `-` or `_` characters
+  - `instrument` (e.g. MSI)
+  - `platform` (e.g. SENTINEL2)
+  - `platformSerialIdentifier` (e.g. S2A)
+  - `processingLevel` (e.g. L1)
+  - `sensorType` (e.g. OPTICAL)
+  - `keywords` (e.g. SENTINEL2 L1C SAFE), which is case insensitive and ignores `-` or `_` characters
 
-For example, the following search request will first search for a product type for platform SENTINEL2 and processingLevel L1
-(there are several product types matching these criteria, e.g., `S2_MSI_L1C`) and then use this product type to execute the actual search.
+For example, the following search request will first search for a product type for platform SENTINEL2 and
+processingLevel L1 (there are several product types matching these criteria, e.g., `S2_MSI_L1C`) and then use this
+product type to execute the actual search.
 
 .. code-block:: console
 
@@ -125,12 +91,22 @@ For example, the following search request will first search for a product type f
         --box 1 43 2 44 \
         --start 2021-03-01 --end 2021-03-31
 
+Download
+--------
+
+.. command-output:: eodag download --help
+
 * To download the result of a previous call to ``search``:
 
 .. code-block:: console
 
         eodag download --conf my_conf.yml --search-results my_search.geojson
 
+Product Types
+-------------
+
+.. command-output:: eodag list --help
+
 * To list all available product types and supported providers:
 
 .. code-block:: console
@@ -153,6 +129,10 @@ For example, the following search request will first search for a product type f
 * EODAG can fetch providers (all or only a given one) to discover available product types, using the following command.
   It will store result in a JSON file (defaults to `ext_product_types.json`):
 
+.. command-output:: eodag discover --help
+
+Examples:
+
 .. code-block:: console
 
         eodag discover
@@ -164,3 +144,22 @@ This file can then be used in EODAG using the environment variable ``EODAG_EXT_P
 Please note that if you did not customize EODAG with new providers settings, this command should not be useful.
 For more information on the product types discovery mechanism, please see
 `Python API User Guide / Providers and products / Product types discovery <notebooks/api_user_guide/2_providers_products_available.html#Product-types-discovery>`_.
+
+Server mode
+-----------
+
+EODAG has a STAC compliant REST API. It can serve configured providers data through
+this STAC API.
+
+.. command-output:: eodag serve-rest --help
+
+And for advanced configuration:
+
+.. command-output:: eodag deploy-wsgi-app --help
+
+See server mode usage examples in `STAC REST API Server <stac_rest.rst>`_.
+
+.. toctree::
+   :maxdepth: 2
+
+   stac_rest
diff --git a/docs/conf.py b/docs/conf.py
index adc7255d..946f4977 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -46,6 +46,7 @@ extensions = [
     "sphinx_copybutton",
     "sphinx_tabs.tabs",
     "sphinx_autodoc_typehints",
+    "sphinxcontrib.programoutput",
     "sphinxemoji.sphinxemoji",
 ]
 
diff --git a/docs/index.rst b/docs/index.rst
index 2f34a070..47df1abf 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -120,9 +120,8 @@ Site contents
    getting_started_guide/index
    api_user_guide
    api_reference/index
-   breaking_changes
    cli_user_guide
-   stac
+   breaking_changes
    tutos
 
 .. toctree::
diff --git a/docs/notebooks/tutos/tuto_stac_client.ipynb b/docs/notebooks/tutos/tuto_stac_client.ipynb
index 0213947e..2b0aefe4 100644
--- a/docs/notebooks/tutos/tuto_stac_client.ipynb
+++ b/docs/notebooks/tutos/tuto_stac_client.ipynb
@@ -5,7 +5,7 @@
    "id": "weird-cleveland",
    "metadata": {},
    "source": [
-    "# EODAG as STAC client"
+    "# STAC client"
    ]
   },
   {
@@ -13,7 +13,7 @@
    "id": "reverse-sailing",
    "metadata": {},
    "source": [
-    "## STAC API\n",
+    "## STAC API client\n",
     "EODAG can perform an item search over a STAC compliant API. Found STAC items are returned as [EOProduct](../../api_reference/eoproduct.rst#eodag.api.product._product.EOProduct) objects with STAC metadata mapped to OGC OpenSearch Extension for Earth Observation.\n",
     "\n",
     "EODAG comes with already configured providers, but you can also add new ones dynamically."
@@ -22973,7 +22973,7 @@
    "id": "offshore-stereo",
    "metadata": {},
    "source": [
-    "## STAC Static catalog"
+    "## STAC Static catalog client"
    ]
   },
   {
diff --git a/docs/stac.rst b/docs/stac.rst
deleted file mode 100644
index 1ff3a24e..00000000
--- a/docs/stac.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-.. _stac:
-
-STAC Support
-============
-
-
-``eodag``'s interfaces are compliant with the `SpatioTemporal Asset Catalog <https://github.com/radiantearth/stac-spec>`_
-(STAC) specification.
-
-STAC client
------------
-
-STAC API providers can be configured to be used for `search` and `download` using EODAG.
-Some providers (*dedl*, *earth_search*, *geodes*, *hydroweb_next*, *planetary_computer*,
-*usgs_satapi_aws*) are already implemented and new providers can be dynamically added by the user.
-Static catalogs can also be fetched by EODAG.
-
-STAC server
------------
-
-EODAG can run as STAC API REST server and give access to configured
-providers data through a STAC compliant search API.
-
-.. toctree::
-   :maxdepth: 2
-
-   STAC client <notebooks/tutos/tuto_stac_client.ipynb>
-   stac_rest
diff --git a/docs/stac_rest.rst b/docs/stac_rest.rst
index 5a305885..1ee21e9a 100644
--- a/docs/stac_rest.rst
+++ b/docs/stac_rest.rst
@@ -1,43 +1,21 @@
 .. _stac_rest:
 
-STAC REST API
-=============
+STAC REST API Server
+====================
 
 .. image:: _static/eodag_stac_server.png
    :width: 800
    :alt: EODAG as STAC server
    :class: no-scaled-link
 
-EODAG has a STAC compliant REST API. It can serve configured providers data through
-this STAC API. To run the server, do:
+EODAG has a STAC compliant REST API available through the CLI. It can serve configured
+providers data through this STAC API
+(See `CLI USer Guide / Server mode <cli_user_guide.rst#server-mode>`_). To run the server, do:
 
 .. code-block:: console
 
     eodag serve-rest
 
-Below is the content of the help message of this command (`eodag serve-rest --help`):
-
-.. code-block:: console
-
-    Usage: eodag serve-rest [OPTIONS]
-
-      Start eodag HTTP server
-
-      Set EODAG_CORS_ALLOWED_ORIGINS environment variable to configure Cross-
-      Origin Resource Sharing allowed origins as comma-separated URLs (e.g.
-      'http://somewhere,htttp://somewhere.else').
-
-    Options:
-      -f, --config PATH   File path to the user configuration file with its
-                          credentials, default is ~/.config/eodag/eodag.yml
-      -l, --locs PATH     File path to the location shapefiles configuration file
-      -d, --daemon        run in daemon mode
-      -w, --world         run uvicorn using IPv4 0.0.0.0 (all network interfaces),
-                          otherwise bind to 127.0.0.1 (localhost).
-      -p, --port INTEGER  The port on which to listen  [default: 5000]
-      --debug             Run in debug mode (for development purpose)
-      --help              Show this message and exit.
-
 Searching
 ---------
 
diff --git a/docs/tutos.rst b/docs/tutos.rst
index 419b2bf5..b4555373 100644
--- a/docs/tutos.rst
+++ b/docs/tutos.rst
@@ -26,6 +26,7 @@ or run locally after being downloaded (see how to :ref:`install_notebooks`).
    of each tutorial.
 
 .. toctree::
+   :maxdepth: 2
 
    notebooks/tutos/tuto_search_location_tile.ipynb
    notebooks/tutos/tuto_cop_dem.ipynb
diff --git a/eodag/api/core.py b/eodag/api/core.py
index cc2e2122..a4ba37cf 100644
--- a/eodag/api/core.py
+++ b/eodag/api/core.py
@@ -69,6 +69,7 @@ from eodag.utils import (
     DEFAULT_MAX_ITEMS_PER_PAGE,
     DEFAULT_PAGE,
     GENERIC_PRODUCT_TYPE,
+    GENERIC_STAC_PROVIDER,
     HTTP_REQ_TIMEOUT,
     MockResponse,
     _deprecated,
@@ -2476,3 +2477,25 @@ class EODataAccessGateway:
             )
         # Remove the ID since this is equal to productType.
         plugin.config.product_type_config.pop("ID", None)
+
+    def import_stac_items(self, items_urls: list[str]) -> SearchResult:
+        """Import STAC items from a list of URLs and convert them to SearchResult.
+
+        :param items_urls: A list of STAC items URLs to import
+        :returns: A SearchResult containing the imported STAC items
+        """
+        json_items = []
+        for item_url in items_urls:
+            json_items.extend(fetch_stac_items(item_url))
+
+        # add a generic STAC provider that might be needed to handle the items
+        self.add_provider(GENERIC_STAC_PROVIDER)
+
+        results = SearchResult([])
+        for json_item in json_items:
+            if search_result := SearchResult._from_stac_item(
+                json_item, self._plugins_manager
+            ):
+                results.extend(search_result)
+
+        return results
diff --git a/eodag/api/product/__init__.py b/eodag/api/product/__init__.py
index e2664e4a..49ffd972 100644
--- a/eodag/api/product/__init__.py
+++ b/eodag/api/product/__init__.py
@@ -17,6 +17,12 @@
 # limitations under the License.
 #
 """EODAG product package"""
+
+from typing import TYPE_CHECKING, Any, Optional
+
+if TYPE_CHECKING:
+    from eodag.plugins.manager import PluginManager
+
 try:
     # import from eodag-cube if installed
     from eodag_cube.api.product import (  # pyright: ignore[reportMissingImports]
@@ -30,3 +36,27 @@ except ImportError:
 
 # exportable content
 __all__ = ["Asset", "AssetsDict", "EOProduct"]
+
+
+def unregistered_product_from_item(
+    feature: dict[str, Any], provider: str, plugins_manager: "PluginManager"
+) -> Optional[EOProduct]:
+    """Create an EOProduct from a STAC item, map its metadata, but without registering its plugins.
+
+    :param feature: The STAC item to convert into an EOProduct.
+    :param provider: The associated provider from which configuration should be used for mapping.
+    :param plugins_manager: The plugins manager instance to use for retrieving search plugins.
+    :returns: An EOProduct instance if the item can be normalized, otherwise None.
+    """
+    for search_plugin in plugins_manager.get_search_plugins(provider=provider):
+        if hasattr(search_plugin, "normalize_results"):
+            products = search_plugin.normalize_results([feature])
+            if len(products) > 0:
+                # properties cleanup
+                for prop in ("start_datetime", "end_datetime"):
+                    products[0].properties.pop(prop, None)
+                # set product type if not already set
+                if products[0].product_type is None:
+                    products[0].product_type = products[0].properties.get("productType")
+                return products[0]
+    return None
diff --git a/eodag/api/search_result.py b/eodag/api/search_result.py
index 75f5aac4..d7ba41e2 100644
--- a/eodag/api/search_result.py
+++ b/eodag/api/search_result.py
@@ -17,23 +17,30 @@
 # limitations under the License.
 from __future__ import annotations
 
+import logging
 from collections import UserList
 from typing import TYPE_CHECKING, Annotated, Any, Iterable, Optional, Union
 
 from shapely.geometry import GeometryCollection, shape
 from typing_extensions import Doc
 
-from eodag.api.product import EOProduct
+from eodag.api.product import EOProduct, unregistered_product_from_item
 from eodag.plugins.crunch.filter_date import FilterDate
 from eodag.plugins.crunch.filter_latest_intersect import FilterLatestIntersect
 from eodag.plugins.crunch.filter_latest_tpl_name import FilterLatestByName
 from eodag.plugins.crunch.filter_overlap import FilterOverlap
 from eodag.plugins.crunch.filter_property import FilterProperty
+from eodag.utils import GENERIC_STAC_PROVIDER, STAC_SEARCH_PLUGINS
+from eodag.utils.exceptions import MisconfiguredError
 
 if TYPE_CHECKING:
     from shapely.geometry.base import BaseGeometry
 
     from eodag.plugins.crunch.base import Crunch
+    from eodag.plugins.manager import PluginManager
+
+
+logger = logging.getLogger("eodag.search_result")
 
 
 class SearchResult(UserList[EOProduct]):
@@ -211,6 +218,153 @@ class SearchResult(UserList[EOProduct]):
 
         return super().extend(other)
 
+    @classmethod
+    def _from_stac_item(
+        cls, feature: dict[str, Any], plugins_manager: PluginManager
+    ) -> SearchResult:
+        """Create a SearchResult from a STAC item.
+
+        :param feature: A STAC item as a dictionary
+        :param plugins_manager: The EODAG plugin manager instance
+        :returns: A SearchResult containing the EOProduct(s) created from the STAC item
+        """
+        # Try importing from EODAG Server
+        if results := _import_stac_item_from_eodag_server(feature, plugins_manager):
+            return results
+
+        # try importing from a known STAC provider
+        if results := _import_stac_item_from_known_provider(feature, plugins_manager):
+            return results
+
+        # try importing from an unknown STAC provider
+        return _import_stac_item_from_unknown_provider(feature, plugins_manager)
+
+
+def _import_stac_item_from_eodag_server(
+    feature: dict[str, Any], plugins_manager: PluginManager
+) -> Optional[SearchResult]:
+    """Import a STAC item from EODAG Server.
+
+    :param feature: A STAC item as a dictionary
+    :param plugins_manager: The EODAG plugin manager instance
+    :returns: A SearchResult containing the EOProduct(s) created from the STAC item
+    """
+    provider = None
+    if backends := feature["properties"].get("federation:backends"):
+        provider = backends[0]
+    elif providers := feature["properties"].get("providers"):
+        provider = providers[0].get("name")
+    if provider is not None:
+        logger.debug("Trying to import STAC item from EODAG Server")
+        # assets coming from a STAC provider
+        assets = {
+            k: v["alternate"]["origin"]
+            for k, v in feature.get("assets", {}).items()
+            if k not in ("thumbnail", "downloadLink")
+            and "origin" in v.get("alternate", {})
+        }
+        if assets:
+            updated_item = {**feature, **{"assets": assets}}
+        else:
+            # item coming from a non-STAC provider
+            updated_item = {**feature}
+            download_link = (
+                feature.get("assets", {})
+                .get("downloadLink", {})
+                .get("alternate", {})
+                .get("origin", {})
+                .get("href")
+            )
+            if download_link:
+                updated_item["assets"] = {}
+                updated_item["links"] = [{"rel": "self", "href": download_link}]
+            else:
+                updated_item = {}
+        try:
+            eo_product = unregistered_product_from_item(
+                updated_item, GENERIC_STAC_PROVIDER, plugins_manager
+            )
+        except MisconfiguredError:
+            eo_product = None
+        if eo_product is not None:
+            eo_product.provider = provider
+            eo_product._register_downloader_from_manager(plugins_manager)
+            return SearchResult([eo_product])
+    return None
+
+
+def _import_stac_item_from_known_provider(
+    feature: dict[str, Any], plugins_manager: PluginManager
+) -> Optional[SearchResult]:
+    """Import a STAC item from an already-configured STAC provider.
+
+    :param feature: A STAC item as a dictionary
+    :param plugins_manager: The EODAG plugin manager instance
+    :returns: A SearchResult containing the EOProduct(s) created from the STAC item
+    """
+    item_hrefs = [f for f in feature.get("links", []) if f.get("rel") == "self"]
+    item_href = item_hrefs[0]["href"] if len(item_hrefs) > 0 else None
+    imported_products = SearchResult([])
+    for search_plugin in plugins_manager.get_search_plugins():
+        # only try STAC search plugins
+        if (
+            search_plugin.config.type in STAC_SEARCH_PLUGINS
+            and search_plugin.provider != GENERIC_STAC_PROVIDER
+            and hasattr(search_plugin, "normalize_results")
+        ):
+            provider_base_url = search_plugin.config.api_endpoint.removesuffix(
+                "/search"
+            )
+            # compare the item href with the provider base URL
+            if item_href and item_href.startswith(provider_base_url):
+                products = search_plugin.normalize_results([feature])
+                if len(products) == 0 or len(products[0].assets) == 0:
+                    continue
+                logger.debug(
+                    "Trying to import STAC item from %s", search_plugin.provider
+                )
+                eo_product = products[0]
+
+                configured_pts = [
+                    k
+                    for k, v in search_plugin.config.products.items()
+                    if v.get("productType") == feature.get("collection")
+                ]
+                if len(configured_pts) > 0:
+                    eo_product.product_type = configured_pts[0]
+                else:
+                    eo_product.product_type = feature.get("collection")
+
+                eo_product._register_downloader_from_manager(plugins_manager)
+                imported_products.append(eo_product)
+    if len(imported_products) > 0:
+        return imported_products
+    return None
+
+
+def _import_stac_item_from_unknown_provider(
+    feature: dict[str, Any], plugins_manager: PluginManager
+) -> SearchResult:
+    """Import a STAC item from an unknown STAC provider.
+
+    :param feature: A STAC item as a dictionary
+    :param plugins_manager: The EODAG plugin manager instance
+    :returns: A SearchResult containing the EOProduct(s) created from the STAC item
+    """
+    try:
+        logger.debug("Trying to import STAC item from unknown provider")
+        eo_product = unregistered_product_from_item(
+            feature, GENERIC_STAC_PROVIDER, plugins_manager
+        )
+    except MisconfiguredError:
+        pass
+    if eo_product is not None:
+        eo_product.product_type = feature.get("collection")
+        eo_product._register_downloader_from_manager(plugins_manager)
+        return SearchResult([eo_product])
+    else:
+        return SearchResult([])
+
 
 class RawSearchResult(UserList[dict[str, Any]]):
     """An object representing a collection of raw/unparsed search results obtained from a provider.
diff --git a/eodag/config.py b/eodag/config.py
index f9d9e011..ddaf7709 100644
--- a/eodag/config.py
+++ b/eodag/config.py
@@ -46,6 +46,7 @@ from jsonpath_ng import JSONPath
 from eodag.api.product.metadata_mapping import mtd_cfg_as_conversion_and_querypath
 from eodag.utils import (
     HTTP_REQ_TIMEOUT,
+    STAC_SEARCH_PLUGINS,
     USER_AGENT,
     cached_yaml_load,
     cached_yaml_load_all,
@@ -819,12 +820,7 @@ def provider_config_init(
         if (
             stac_search_default_conf is not None
             and provider_config.search
-            and provider_config.search.type
-            in [
-                "StacSearch",
-                "StacListAssets",
-                "StaticStacSearch",
-            ]
+            and provider_config.search.type in STAC_SEARCH_PLUGINS
         ):
             # search config set to stac defaults overriden with provider config
             per_provider_stac_provider_config = deepcopy(stac_search_default_conf)
diff --git a/eodag/plugins/search/build_search_result.py b/eodag/plugins/search/build_search_result.py
index db008784..63bcfd94 100644
--- a/eodag/plugins/search/build_search_result.py
+++ b/eodag/plugins/search/build_search_result.py
@@ -317,17 +317,17 @@ def append_time(input_date: date, time: Optional[str]) -> datetime:
 
 
 def parse_date(
-    date_str: str, time: Optional[Union[str, list[str]]]
+    date: str, time: Optional[Union[str, list[str]]]
 ) -> tuple[datetime, datetime]:
     """Parses a date string in formats YYYY-MM-DD, YYYMMDD, solo or in start/end or start/to/end intervals."""
-    if "to" in date_str:
-        start_date_str, end_date_str = date_str.split("/to/")
-    elif "/" in date_str:
-        dates = date_str.split("/")
+    if "to" in date:
+        start_date_str, end_date_str = date.split("/to/")
+    elif "/" in date:
+        dates = date.split("/")
         start_date_str = dates[0]
         end_date_str = dates[-1]
     else:
-        start_date_str = end_date_str = date_str
+        start_date_str = end_date_str = date
 
     # Update YYYYMMDD formatted dates
     if re.match(r"^\d{8}$", start_date_str):
@@ -401,6 +401,8 @@ def ecmwf_temporal_to_eodag(
     start = end = None
 
     if date := params.get("date"):
+        if isinstance(date, list):
+            date = "/".join(date)
         start, end = parse_date(date, params.get("time"))
 
     elif year := (params.get("year") or params.get("hyear")):
diff --git a/eodag/plugins/search/qssearch.py b/eodag/plugins/search/qssearch.py
index 717b0dab..b57b5d8f 100644
--- a/eodag/plugins/search/qssearch.py
+++ b/eodag/plugins/search/qssearch.py
@@ -1173,7 +1173,10 @@ class QueryStringSearch(Search):
 
         collection = getattr(self.config, "collection", None)
         if collection is None:
-            collection = prep.product_type_def_params.get("collection") or product_type
+            collection = (
+                getattr(prep, "product_type_def_params", {}).get("collection")
+                or product_type
+            )
 
         if collection is None:
             return ()
diff --git a/eodag/resources/product_types.yml b/eodag/resources/product_types.yml
index 00896815..0b1ca8c5 100644
--- a/eodag/resources/product_types.yml
+++ b/eodag/resources/product_types.yml
@@ -4917,6 +4917,76 @@ EUSTAT_SHARE_ENERGY_FROM_RENEWABLE:
   missionStartDate: "2004-01-01T00:00:00Z"
   missionEndDate: "2023-12-31T23:59:59Z"
 
+EUSTAT_AVAILABLE_BEDS_HOSPITALS_NUTS2:
+  abstract: |
+    Non-expenditure healthcare data provide information on institutions providing healthcare in countries, on resources used and on output produced in the framework of healthcare provision. \nData on healthcare form a major element of public health information as they describe the capacities available for different types of healthcare provision as well as potential 'bottlenecks' observed. The quantity and quality of healthcare services provided and the work sharing established between the different institutions are a subject of ongoing debate in all countries. Sustainability - continuously providing the necessary monetary and personal resources needed - and meeting the challenges of ageing societies are the primary perspectives used when analysing and using the data. \nThe resource-related data refer to both human and technical resources, i.e. they relate to: \n- Health care staff: 'manpower' active in the health care sector (doctors, dentists, nurses, etc.);\n- Heath workforce migration: migration movements of doctors and nurses;\n- Healthcare facilities: technical capacity dimensions (hospital beds, beds in nursing and residential care facilities, etc.).\nThe output-related data ('activities') refer to contacts between patients and the healthcare system, and to the treatment thereby received. Data are available for hospital discharges of in-patients and day cases, average length of stay of in-patients, consultations with medical professionals, and medical procedures performed in hospitals.\nAnnual national and regional data are provided in absolute numbers, percentages, and in population-standardised rates (per 100 000 inhabitants).\nWherever applicable, the definitions and classifications of the System of Health Accounts (SHA) are followed, e.g. International Classification for Health Accounts - Providers of health care (ICHA-HP). For hospital discharges, the International Shortlist for Hospital Morbidity Tabulation (ISHMT) is used. Surgical procedures are classified according to a shortlist mapped to ICD-9-CM.\nThese healthcare data are largely based on administrative data sources in the countries. Therefore, they reflect the country-specific way of organising healthcare and may not always be completely comparable.
+  instrument:
+  platform: Eurostat
+  platformSerialIdentifier: Eurostat
+  processingLevel:
+  keywords: Eurostat, Health care, Hospital, Bed, Health
+  sensorType:
+  license: proprietary
+  title: Available beds in hospitals by NUTS 2 region
+  missionStartDate: "2013-01-01T00:00:00Z"
+  missionEndDate: "2022-12-31T23:59:59Z"
+
+EUSTAT_BATHING_SITES_WATER_QUALITY:
+  abstract: |
+    The indicator measures the number and proportion of coastal and inland bathing sites with excellent water quality. The indicator assessment is based on microbiological parameters (intestinal enterococci and Escherichia coli). The new Bathing Water Directive requires Member States to identify and assess the quality of all inland and marine bathing waters and to classify these waters as ‘poor’, ‘sufficient’, ‘good’ or ‘excellent’.
+  instrument:
+  platform: Eurostat
+  platformSerialIdentifier: Eurostat
+  processingLevel:
+  keywords: Eurostat, Bath, Water, Water quality
+  sensorType:
+  license: proprietary
+  title: Bathing sites with excellent water quality by location
+  missionStartDate: "2011-01-01T00:00:00Z"
+  missionEndDate: "2023-12-31T23:59:59Z"
+
+EUSTAT_POP_DENSITY_NUTS3:
+  abstract: |
+    Eurostat’s annual data collections on population. Member States send population data to Eurostat data as on of 31 December for the reference year under Regulation 1260/2013 on European demographic statistics. The data are conventionally published by Eurostat as population on 1 January of the following year (reference year + 1). \nThe aim is to collect annual mandatory and voluntary demographic data from the national statistical institutes. Mandatory data are those defined by the legislation listed under ‘6.1. Institutional mandate — legal acts and other agreements’. \nThe completeness of the demographic data collected on a voluntary basis depends on the availability and completeness of information provided by the national statistical institutes.\nFor more information on mandatory/voluntary data collection, see 6.1. Institutional mandate — legal acts and other agreements. \nThe following statistics are available. \nPopulation on 1 January by sex and by:\n- single age and educational attainment / marital status / broad group of citizenship / broad group of country of birth;\n  - five-year age group and citizenship / country of birth;\n  - citizenship and broad group of country of birth / country of birth and broad group of citizenship;\n  - broad age group and NUTS 3 (under regional data population folder);\n  - single age and NUTS 2 (under regional data population folder);\n  - five-year age group and NUTS 2 / NUTS 3 (under regional data population folder).\nPopulation structure statistics: median age of population, proportion of population by various age groups, old age dependency ratio.
+  instrument:
+  platform: Eurostat
+  platformSerialIdentifier: Eurostat
+  processingLevel:
+  keywords: Eurostat, Population, Density, NUTS 3
+  sensorType:
+  license: proprietary
+  title: Population density by NUTS 3 region
+  missionStartDate: "1990-01-01T00:00:00Z"
+  missionEndDate: "2021-12-31T23:59:59Z"
+
+EUSTAT_SOIL_SEALING_INDEX:
+  abstract: |
+    The indicator estimates the increase in sealed soil surfaces with impervious materials due to urban development and construction (e.g. buildings, constructions and laying of completely or partially impermeable artificial material, such as asphalt, metal, glass, plastic or concrete). This provides an indication of the rate of soil sealing, when areas change land use towards artificial and urban land use. The indicator builds on data from the imperviousness High Resolution Layer (a product of the Copernicus Land Monitoring Service). The indicator is presented in the following units: Index 2006=100 % of total surface total sealed surface in km2.
+  instrument:
+  platform: Eurostat
+  platformSerialIdentifier: Eurostat
+  processingLevel:
+  keywords: Eurostat, soil, soil sealing, SDG, EU Sustainable Development Goals
+  sensorType:
+  license: proprietary
+  title: Soil sealing index
+  missionStartDate: "2006-01-01T00:00:00Z"
+  missionEndDate: "2018-12-31T23:59:59Z"
+
+EUSTAT_SURFACE_TERRESTRIAL_PROTECTED_AREAS:
+  abstract: |
+    The indicator measures the surface of terrestrial protected areas. The indicator comprises nationally designated protected areas and Natura 2000 sites. A nationally designated area is an area protected by national legislation. The Natura 2000 network comprises both marine and terrestrial protected areas designated under the EU Habitats and Birds Directives with the goal to maintain or restore a favourable conservation status for habitat types and species of EU interest.
+  instrument:
+  platform: Eurostat
+  platformSerialIdentifier: Eurostat
+  processingLevel:
+  keywords: Eurostat, CO2, terrestrial, protected areas
+  sensorType:
+  license: proprietary
+  title: Surface of the terrestrial protected areas
+  missionStartDate: "2013-01-01T00:00:00Z"
+  missionEndDate: "2022-12-31T23:59:59Z"
+
 # MARK: ISIMIP -------------------------------------------------------------------------
 ISIMIP_CLIMATE_FORCING_ISIMIP3B:
   abstract: |
diff --git a/eodag/resources/providers.yml b/eodag/resources/providers.yml
index 7f2a965e..da9c1f80 100644
--- a/eodag/resources/providers.yml
+++ b/eodag/resources/providers.yml
@@ -5072,6 +5072,16 @@
       productType: STAT.EUSTAT.DAT.POP_CHANGE_DEMO_BALANCE_CRUDE_RATES_NUTS3
     EUSTAT_SHARE_ENERGY_FROM_RENEWABLE:
       productType: STAT.EUSTAT.DAT.SHARE_ENERGY_FROM_RENEWABLE
+    EUSTAT_AVAILABLE_BEDS_HOSPITALS_NUTS2:
+      productType: STAT.EUSTAT.DAT.AVAILABLE_BEDS_HOSPITALS_NUTS2
+    EUSTAT_BATHING_SITES_WATER_QUALITY:
+      productType: STAT.EUSTAT.DAT.BATHING_SITES_WATER_QUALITY
+    EUSTAT_SOIL_SEALING_INDEX:
+      productType: STAT.EUSTAT.DAT.SOIL_SEALING_INDEX
+    EUSTAT_SURFACE_TERRESTRIAL_PROTECTED_AREAS:
+      productType: STAT.EUSTAT.DAT.SURFACE_TERRESTRIAL_PROTECTED_AREAS
+    EUSTAT_POP_DENSITY_NUTS3:
+      productType: STAT.EUSTAT.DAT.POP_DENSITY_NUTS3
     # ISIMIP
     ISIMIP_CLIMATE_FORCING_ISIMIP3B:
       productType: EO.ISIMIP.DAT.CLIMATE-FORCING_ISIMIP3b
diff --git a/eodag/rest/server.py b/eodag/rest/server.py
index c42bc853..ecaad46f 100755
--- a/eodag/rest/server.py
+++ b/eodag/rest/server.py
@@ -383,24 +383,26 @@ async def list_collection_queryables(
     :returns: A json object containing the list of available queryable properties for the specified collection.
     """
     logger.info(f"{request.method} {request.state.url}")
-    # split by `,` to handle list of parameters
-    additional_params = {k: v.split(",") for k, v in dict(request.query_params).items()}
-    provider = additional_params.pop("provider", None)
 
-    datetime = additional_params.pop("datetime", None)
+    params: dict[str, list[Any]] = {}
+    for k, v in request.query_params.multi_items():
+        params.setdefault(k, []).append(v)
+
+    provider = params.pop("provider", [None])[0]
+    datetime = params.pop("datetime", [None])[0]
 
     queryables = await get_queryables(
         request,
         QueryablesGetParams.model_validate(
             {
-                **additional_params,
+                **params,
                 **{
                     "collection": collection_id,
-                    "datetime": datetime[0] if datetime else None,
+                    "datetime": datetime,
                 },
             }
         ),
-        provider=provider[0] if provider else None,
+        provider=provider,
     )
 
     return ORJSONResponse(queryables)
diff --git a/eodag/utils/__init__.py b/eodag/utils/__init__.py
index 1b1cf68c..0d70f717 100644
--- a/eodag/utils/__init__.py
+++ b/eodag/utils/__init__.py
@@ -110,6 +110,13 @@ logger = py_logging.getLogger("eodag.utils")
 DEFAULT_PROJ = "EPSG:4326"
 
 GENERIC_PRODUCT_TYPE = "GENERIC_PRODUCT_TYPE"
+GENERIC_STAC_PROVIDER = "generic_stac_provider"
+
+STAC_SEARCH_PLUGINS = [
+    "StacSearch",
+    "StacListAssets",
+    "StaticStacSearch",
+]
 
 eodag_version = metadata("eodag")["Version"]
 USER_AGENT = {"User-Agent": f"eodag/{eodag_version}"}
diff --git a/setup.cfg b/setup.cfg
index e0d4daf7..579702d1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -129,12 +129,13 @@ stubs =
     types-urllib3
 docs =
     eodag[all,stubs]
+    nbsphinx
     sphinx
+    sphinx-autodoc-typehints
     sphinx-book-theme >= 1.0.0
     sphinx-copybutton
     sphinx-tabs
-    nbsphinx
-    sphinx_autodoc_typehints
+    sphinxcontrib-programoutput
     sphinxemoji
 
 [options.packages.find]


Test patch:
diff --git a/tests/context.py b/tests/context.py
index f6fb26d8..d86451f0 100644
--- a/tests/context.py
+++ b/tests/context.py
@@ -94,6 +94,7 @@ from eodag.utils import (
     parse_qsl,
     urlsplit,
     GENERIC_PRODUCT_TYPE,
+    GENERIC_STAC_PROVIDER,
     flatten_top_directories,
     deepcopy,
     cached_parse,
diff --git a/tests/integration/test_core_search_results.py b/tests/integration/test_core_search_results.py
index 0b4589cf..6b296313 100644
--- a/tests/integration/test_core_search_results.py
+++ b/tests/integration/test_core_search_results.py
@@ -24,7 +24,14 @@ import tempfile
 from shapely import geometry
 
 from tests import TEST_RESOURCES_PATH, EODagTestCase
-from tests.context import EODataAccessGateway, EOProduct, PluginTopic, SearchResult
+from tests.context import (
+    GENERIC_STAC_PROVIDER,
+    Download,
+    EODataAccessGateway,
+    EOProduct,
+    PluginTopic,
+    SearchResult,
+)
 from tests.utils import mock
 
 
@@ -334,3 +341,172 @@ class TestCoreSearchResults(EODagTestCase):
             mock_urlopen.call_args_list[-1][0][0].full_url,
         )
         self.assertIsNotNone(search_results.number_matched)
+
+    @mock.patch(
+        "eodag.api.core.fetch_stac_items",
+        autospec=True,
+        side_effect=[
+            [
+                {
+                    "type": "Feature",
+                    "id": "stac-fastapi-eodag-id",
+                    "geometry": {
+                        "type": "Point",
+                        "coordinates": [0, 0],
+                    },
+                    "properties": {
+                        "title": "Stac-FastApi-Eodag item",
+                        "federation:backends": ["cop_dataspace"],
+                    },
+                    "collection": "foo-collection",
+                    "assets": {
+                        "downloadLink": {
+                            "title": "Download link",
+                            "href": "https://stac-fastapi-eodag-server/download-link",
+                            "type": "application/zip",
+                            "alternate": {
+                                "origin": {
+                                    "title": "Origin asset link",
+                                    "href": "https://provider-url/origin-link",
+                                    "type": "application/zip",
+                                },
+                            },
+                        },
+                        "thumbnail": {
+                            "title": "Thumbnail",
+                            "href": "https://stac-fastapi-eodag-server/thumbnail",
+                            "type": "application/zip",
+                        },
+                    },
+                }
+            ],
+            [
+                {
+                    "type": "Feature",
+                    "id": "legacy-server-id",
+                    "geometry": {
+                        "type": "Point",
+                        "coordinates": [0, 0],
+                    },
+                    "properties": {
+                        "title": "Legacy-Eodag-server item",
+                        "providers": [{"name": "earth_search"}],
+                    },
+                    "collection": "bar-collection",
+                    "assets": {
+                        "downloadLink": {
+                            "title": "Download link",
+                            "href": "https://legacy-server/download-link",
+                            "type": "application/zip",
+                            "alternate": {
+                                "origin": {
+                                    "title": "Origin asset link",
+                                    "href": "https://provider-url/origin-link",
+                                    "type": "application/zip",
+                                },
+                            },
+                        },
+                        "asset-1": {
+                            "title": "Asset 1",
+                            "href": "https://legacy-server/asset-1",
+                            "type": "application/zip",
+                            "alternate": {
+                                "origin": {
+                                    "title": "Origin asset link",
+                                    "href": "https://provider-url/asset-1-link",
+                                    "type": "application/zip",
+                                },
+                            },
+                        },
+                        "asset-2": {
+                            "title": "Asset 2",
+                            "href": "https://legacy-server/asset-2",
+                            "type": "application/zip",
+                            "alternate": {
+                                "origin": {
+                                    "title": "Origin asset link",
+                                    "href": "https://provider-url/asset-2-link",
+                                    "type": "application/zip",
+                                },
+                            },
+                        },
+                    },
+                }
+            ],
+        ],
+    )
+    def test_core_import_stac_items_from_eodag_server(self, mock_fetch_stac_items):
+        """The core api must import STAC items from EODAG server"""
+        results = self.dag.import_stac_items(
+            [
+                "https://stac-fastapi-eodag-server/collections/foo/items/stac-fastapi-eodag-id",
+                "https://legacy-server/collections/foo/items/legacy-server-id",
+            ]
+        )
+        self.assertEqual(len(results), 2)
+
+        self.assertEqual(results[0].provider, "cop_dataspace")
+        self.assertEqual(results[0].properties["id"], "stac-fastapi-eodag-id")
+        self.assertEqual(results[0].product_type, "foo-collection")
+        self.assertEqual(len(results[0].assets), 0)
+        self.assertEqual(results[0].location, "https://provider-url/origin-link")
+        self.assertIsInstance(results[0].downloader, Download)
+
+        self.assertEqual(results[1].provider, "earth_search")
+        self.assertEqual(results[1].properties["id"], "legacy-server-id")
+        self.assertEqual(results[1].product_type, "bar-collection")
+        self.assertEqual(len(results[1].assets), 2)
+        self.assertEqual(
+            results[1].assets["asset-1-link"]["href"],
+            "https://provider-url/asset-1-link",
+        )
+        self.assertEqual(
+            results[1].assets["asset-2-link"]["href"],
+            "https://provider-url/asset-2-link",
+        )
+        self.assertIsInstance(results[1].downloader, Download)
+
+    @mock.patch("eodag.api.core.fetch_stac_items", autospec=True)
+    def test_core_import_stac_items_from_known_provider(self, mock_fetch_stac_items):
+        """The core api must import STAC items from a knwown provider"""
+        earth_search_resp_search_file = os.path.join(
+            TEST_RESOURCES_PATH, "provider_responses", "earth_search_search.json"
+        )
+        with open(earth_search_resp_search_file, encoding="utf-8") as f:
+            mock_fetch_stac_items.return_value = [json.load(f)["features"][0]]
+
+        results = self.dag.import_stac_items(
+            [
+                "https://earth-search.aws.element84.com/v1/collections/sentinel-2-l1c/items/S2B_27VWK_20240206_0_L1C",
+            ]
+        )
+        self.assertEqual(len(results), 1)
+
+        self.assertEqual(results[0].provider, "earth_search")
+        self.assertEqual(results[0].properties["id"], "S2B_27VWK_20240206_0_L1C")
+        self.assertEqual(results[0].product_type, "S2_MSI_L1C")
+        self.assertEqual(len(results[0].assets), 17)
+        self.assertTrue(
+            all(v["href"].startswith("s3://") for v in results[0].assets.values())
+        )
+        self.assertIsInstance(results[0].downloader, Download)
+
+    @mock.patch("eodag.api.core.fetch_stac_items", autospec=True)
+    def test_core_import_stac_items_from_unknown_provider(self, mock_fetch_stac_items):
+        """The core api must import STAC items from an unknwown provider"""
+        stac_singlefile = os.path.join(TEST_RESOURCES_PATH, "stac_singlefile.json")
+        with open(stac_singlefile, encoding="utf-8") as f:
+            mock_fetch_stac_items.return_value = [json.load(f)["features"][0]]
+
+        results = self.dag.import_stac_items(
+            [
+                "./tests/resources/stac_singlefile.json",
+            ]
+        )
+        self.assertEqual(len(results), 1)
+
+        self.assertEqual(results[0].provider, GENERIC_STAC_PROVIDER)
+        self.assertEqual(results[0].properties["id"], "S2B_9VXK_20171013_0")
+        self.assertEqual(results[0].product_type, "sentinel-2-l1c")
+        self.assertEqual(len(results[0].assets), 1)
+        self.assertIsInstance(results[0].downloader, Download)
diff --git a/tests/resources/stac_singlefile.json b/tests/resources/stac_singlefile.json
index ce28989e..6e9e8f65 100644
--- a/tests/resources/stac_singlefile.json
+++ b/tests/resources/stac_singlefile.json
@@ -12,8 +12,8 @@
             "type": "Feature",
             "stac_version": "1.0.0",
             "id": "S2B_9VXK_20171013_0",
+            "collection": "sentinel-2-l1c",
             "properties": {
-                "collection": "sentinel-2-l1c",
                 "datetime": "2017-10-13T20:03:46.461000Z",
                 "eo:cloud_cover": 41.52,
                 "sentinel:utm_zone": 9,
@@ -165,8 +165,8 @@
             "type": "Feature",
             "stac_version": "1.0.0",
             "id": "S2A_9VXK_20171015_0",
+            "collection": "sentinel-2-l1c",
             "properties": {
-                "collection": "sentinel-2-l1c",
                 "datetime": "2017-10-15T19:54:08.461000Z",
                 "eo:cloud_cover": 95.78,
                 "sentinel:utm_zone": 9,
@@ -318,8 +318,8 @@
             "type": "Feature",
             "stac_version": "1.0.0",
             "id": "S2B_9VXK_20171016_0",
+            "collection": "sentinel-2-l1c",
             "properties": {
-                "collection": "sentinel-2-l1c",
                 "datetime": "2017-10-16T20:16:16.730000Z",
                 "eo:cloud_cover": 74.99,
                 "sentinel:utm_zone": 9,
@@ -467,8 +467,8 @@
             "type": "Feature",
             "stac_version": "1.0.0",
             "id": "S2B_9VXK_20171017_0",
+            "collection": "sentinel-2-l1c",
             "properties": {
-                "collection": "sentinel-2-l1c",
                 "datetime": "2017-10-17T19:44:04.459000Z",
                 "eo:cloud_cover": 94.45,
                 "sentinel:utm_zone": 9,
@@ -632,8 +632,8 @@
             "type": "Feature",
             "stac_version": "1.0.0",
             "id": "S2A_9VXK_20171002_0",
+            "collection": "sentinel-2-l1c",
             "properties": {
-                "collection": "sentinel-2-l1c",
                 "datetime": "2017-10-02T19:42:34.456000Z",
                 "eo:cloud_cover": 100.0,
                 "sentinel:utm_zone": 9,
diff --git a/tests/units/test_core.py b/tests/units/test_core.py
index 81863513..c11ba84b 100644
--- a/tests/units/test_core.py
+++ b/tests/units/test_core.py
@@ -156,11 +156,16 @@ class TestCore(TestCoreBase):
         "ERA5_PL_MONTHLY": ["cop_cds", "dedl", "wekeo_ecmwf"],
         "ERA5_SL": ["cop_cds", "dedl", "wekeo_ecmwf"],
         "ERA5_SL_MONTHLY": ["cop_cds", "dedl", "wekeo_ecmwf"],
+        "EUSTAT_AVAILABLE_BEDS_HOSPITALS_NUTS2": ["dedl"],
+        "EUSTAT_BATHING_SITES_WATER_QUALITY": ["dedl"],
         "EUSTAT_GREENHOUSE_GAS_EMISSION_AGRICULTURE": ["dedl"],
         "EUSTAT_POP_AGE_GROUP_SEX_NUTS3": ["dedl"],
         "EUSTAT_POP_AGE_SEX_NUTS2": ["dedl"],
         "EUSTAT_POP_CHANGE_DEMO_BALANCE_CRUDE_RATES_NUTS3": ["dedl"],
+        "EUSTAT_POP_DENSITY_NUTS3": ["dedl"],
         "EUSTAT_SHARE_ENERGY_FROM_RENEWABLE": ["dedl"],
+        "EUSTAT_SOIL_SEALING_INDEX": ["dedl"],
+        "EUSTAT_SURFACE_TERRESTRIAL_PROTECTED_AREAS": ["dedl"],
         "FIRE_HISTORICAL": ["cop_ewds", "dedl", "wekeo_ecmwf"],
         "FIRE_SEASONAL": ["cop_ewds"],
         "GLACIERS_DIST_RANDOLPH": ["cop_cds", "dedl", "wekeo_ecmwf"],
diff --git a/tests/units/test_search_plugins.py b/tests/units/test_search_plugins.py
index 0f5d15be..0a86ea3c 100644
--- a/tests/units/test_search_plugins.py
+++ b/tests/units/test_search_plugins.py
@@ -2681,6 +2681,15 @@ class TestSearchPluginECMWFSearch(unittest.TestCase):
             ecmwf_temporal_to_eodag(dict(date="20220215/to/20220216")),
             ("2022-02-15T00:00:00Z", "2022-02-16T00:00:00Z"),
         )
+        # List of dates
+        self.assertEqual(
+            ecmwf_temporal_to_eodag(dict(date=["20220215", "20220216"])),
+            ("2022-02-15T00:00:00Z", "2022-02-16T00:00:00Z"),
+        )
+        self.assertEqual(
+            ecmwf_temporal_to_eodag(dict(date=["20220215"])),
+            ("2022-02-15T00:00:00Z", "2022-02-15T00:00:00Z"),
+        )
 
     def test_plugins_search_ecmwfsearch_get_available_values_from_contraints(self):
         """ECMWFSearch must return available values from constraints"""