diff --git a/.docs/changelog.md b/.docs/changelog.md
index 8c114c004061347b51db487ddafdae19a57721fe..941b3ccc59758e4bf709514ac296b738e13158cc 100644
--- a/.docs/changelog.md
+++ b/.docs/changelog.md
@@ -2,18 +2,20 @@
 author: Martin Weise
 ---
 
-## v1.7.1 (2025-03-04)
-
-#### Fixes
-
-* Fixed a bug where quick interaction with the UI caused the user to trigger the brute-force login detection
-  in [#501](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/issues/501).
+## v1.7.1 (2025-03-06)
 
 #### Features
 
+* Added support to download `pandas` DataFrame by PID
+  in [#503](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/issues/503).
 * Added the possibility to create and fill a table from a `pandas` DataFrame (or optionally just create the schema)
   in [#496](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/issues/496).
 
+#### Fixes
+
+* Fixed a bug where quick interaction with the UI caused the user to trigger the brute-force login detection
+  in [#501](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/issues/501).
+
 ## v1.7.0 (2025-03-03)
 
 [:simple-gitlab: GitLab Release](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/tags/v1.7.1)
diff --git a/dbrepo-analyse-service/Pipfile.lock b/dbrepo-analyse-service/Pipfile.lock
index 94704d5c24469e8669f7037f90a622361f87a2cb..995400fd16e50f79ff7fe5b0b4114ef6c407041e 100644
--- a/dbrepo-analyse-service/Pipfile.lock
+++ b/dbrepo-analyse-service/Pipfile.lock
@@ -425,7 +425,7 @@
         },
         "dbrepo": {
             "hashes": [
-                "sha256:4f5ee48e9a68a44c2d1184923b90729d724553862742738d8f942273a586eb3d"
+                "sha256:e70ea4f7030191eb80116e5d0a4b17b041c94c80359d5d8e707d62218edd9a54"
             ],
             "path": "./lib/dbrepo-1.7.1.tar.gz"
         },
diff --git a/dbrepo-analyse-service/lib/dbrepo-1.7.1-py3-none-any.whl b/dbrepo-analyse-service/lib/dbrepo-1.7.1-py3-none-any.whl
index 8e77c6e1f6a01b3d9739280f29539985c359d2c8..61f52896c18ecbec8090177b38b8dbaeb0e1a95e 100644
Binary files a/dbrepo-analyse-service/lib/dbrepo-1.7.1-py3-none-any.whl and b/dbrepo-analyse-service/lib/dbrepo-1.7.1-py3-none-any.whl differ
diff --git a/dbrepo-analyse-service/lib/dbrepo-1.7.1.tar.gz b/dbrepo-analyse-service/lib/dbrepo-1.7.1.tar.gz
index ede846d6168615c3e3bac49872362163b0a9f005..6708e1d892771d6cdf9293a6e9f5197f4dd9e304 100644
Binary files a/dbrepo-analyse-service/lib/dbrepo-1.7.1.tar.gz and b/dbrepo-analyse-service/lib/dbrepo-1.7.1.tar.gz differ
diff --git a/dbrepo-auth-service/listeners/target/create-event-listener.jar b/dbrepo-auth-service/listeners/target/create-event-listener.jar
index b117c50b6646de91273c95dc5749cd54868012be..0410b9e37b8fedf3956c870e2faf7aa9e8ab89b6 100644
Binary files a/dbrepo-auth-service/listeners/target/create-event-listener.jar and b/dbrepo-auth-service/listeners/target/create-event-listener.jar differ
diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java
index 7ca50f8aa864f88bd75d873c93e9caa736fde258..eb9971869f51a0291eec0b69a494656cc308b984 100644
--- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java
+++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java
@@ -11,7 +11,6 @@ import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.exception.*;
 import at.tuwien.gateway.MetadataServiceGateway;
 import at.tuwien.mapper.MariaDbMapper;
-import at.tuwien.mapper.MetadataMapper;
 import at.tuwien.service.CacheService;
 import at.tuwien.service.DatabaseService;
 import at.tuwien.service.StorageService;
@@ -33,10 +32,7 @@ import lombok.extern.log4j.Log4j2;
 import org.apache.spark.sql.Dataset;
 import org.apache.spark.sql.Row;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
+import org.springframework.http.*;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
@@ -44,7 +40,6 @@ import java.security.Principal;
 import java.sql.SQLException;
 import java.time.Instant;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 @Log4j2
@@ -56,7 +51,6 @@ public class SubsetEndpoint extends RestEndpoint {
     private final CacheService cacheService;
     private final MariaDbMapper mariaDbMapper;
     private final SubsetService subsetService;
-    private final MetadataMapper metadataMapper;
     private final StorageService storageService;
     private final DatabaseService databaseService;
     private final EndpointValidator endpointValidator;
@@ -64,12 +58,11 @@ public class SubsetEndpoint extends RestEndpoint {
 
     @Autowired
     public SubsetEndpoint(CacheService cacheService, MariaDbMapper mariaDbMapper, SubsetService subsetService,
-                          MetadataMapper metadataMapper, StorageService storageService, DatabaseService databaseService,
+                          StorageService storageService, DatabaseService databaseService,
                           EndpointValidator endpointValidator, MetadataServiceGateway metadataServiceGateway) {
         this.cacheService = cacheService;
         this.mariaDbMapper = mariaDbMapper;
         this.subsetService = subsetService;
-        this.metadataMapper = metadataMapper;
         this.storageService = storageService;
         this.databaseService = databaseService;
         this.endpointValidator = endpointValidator;
@@ -162,14 +155,12 @@ public class SubsetEndpoint extends RestEndpoint {
     })
     public ResponseEntity<?> findById(@NotNull @PathVariable("databaseId") UUID databaseId,
                                       @NotNull @PathVariable("subsetId") UUID subsetId,
-                                      @NotNull @RequestHeader("Accept") String accept,
                                       @RequestParam(required = false) Instant timestamp,
                                       Principal principal)
             throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException,
-            QueryNotFoundException, FormatNotAvailableException, StorageUnavailableException, UserNotFoundException,
-            MetadataServiceException, TableNotFoundException, QueryMalformedException, NotAllowedException {
-        log.debug("endpoint find subset in database, databaseId={}, subsetId={}, accept={}, timestamp={}", databaseId,
-                subsetId, accept, timestamp);
+            QueryNotFoundException, UserNotFoundException, MetadataServiceException, NotAllowedException {
+        log.debug("endpoint find subset in database, databaseId={}, subsetId={}, timestamp={}", databaseId,
+                subsetId, timestamp);
         final DatabaseDto database = cacheService.getDatabase(databaseId);
         endpointValidator.validateOnlyPrivateSchemaAccess(database, principal);
         final QueryDto subset;
@@ -185,27 +176,7 @@ public class SubsetEndpoint extends RestEndpoint {
             timestamp = Instant.now();
             log.debug("timestamp not set: default to {}", timestamp);
         }
-        if (accept == null || accept.isBlank()) {
-            accept = MediaType.APPLICATION_JSON_VALUE;
-            log.debug("accept header not set: default to {}", accept);
-        }
-        switch (accept) {
-            case MediaType.APPLICATION_JSON_VALUE:
-                log.trace("accept header matches json");
-                return ResponseEntity.ok(subset);
-            case "text/csv":
-                log.trace("accept header matches csv");
-                final String query = mariaDbMapper.rawSelectQuery(subset.getQuery(), timestamp, null, null);
-                final Dataset<Row> dataset = subsetService.getData(database, query);
-                final ExportResourceDto resource = storageService.transformDataset(dataset);
-                final HttpHeaders headers = new HttpHeaders();
-                headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\"");
-                log.trace("export table resulted in resource {}", resource);
-                return ResponseEntity.ok()
-                        .headers(headers)
-                        .body(resource.getResource());
-        }
-        throw new FormatNotAvailableException("Must provide either application/json or text/csv value for header 'Accept': provided " + accept + " instead");
+        return ResponseEntity.ok(subset);
     }
 
     @PostMapping
@@ -250,18 +221,18 @@ public class SubsetEndpoint extends RestEndpoint {
                             mediaType = "application/json",
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
-    public ResponseEntity<List<Map<String, Object>>> create(@NotNull @PathVariable("databaseId") UUID databaseId,
-                                                            @Valid @RequestBody SubsetDto data,
-                                                            Principal principal,
-                                                            @NotNull HttpServletRequest request,
-                                                            @RequestParam(required = false) Instant timestamp,
-                                                            @RequestParam(required = false) Long page,
-                                                            @RequestParam(required = false) Long size)
+    public ResponseEntity<?> create(@NotNull @PathVariable("databaseId") UUID databaseId,
+                                    @Valid @RequestBody SubsetDto data,
+                                    Principal principal,
+                                    @NotNull HttpServletRequest request,
+                                    @RequestParam(required = false) Instant timestamp,
+                                    @RequestParam(required = false) Long page,
+                                    @RequestParam(required = false) Long size)
             throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException,
             QueryNotFoundException, StorageUnavailableException, QueryMalformedException, StorageNotFoundException,
             QueryStoreInsertException, TableMalformedException, PaginationException, QueryNotSupportedException,
             NotAllowedException, UserNotFoundException, MetadataServiceException, TableNotFoundException,
-            ViewMalformedException, ViewNotFoundException, ImageNotFoundException {
+            ViewMalformedException, ViewNotFoundException, ImageNotFoundException, FormatNotAvailableException {
         log.debug("endpoint create subset in database, databaseId={}, page={}, size={}, timestamp={}", databaseId,
                 page, size, timestamp);
         /* check */
@@ -291,7 +262,7 @@ public class SubsetEndpoint extends RestEndpoint {
         endpointValidator.validateOnlyPrivateSchemaAccess(database, principal);
         try {
             final UUID subsetId = subsetService.create(database, data, timestamp, userId);
-            return getData(databaseId, subsetId, principal, request, timestamp, page, size);
+            return getData(databaseId, subsetId, principal, "application/json", request, timestamp, page, size);
         } catch (SQLException e) {
             log.error("Failed to establish connection to database: {}", e.getMessage());
             throw new DatabaseUnavailableException("Failed to establish connection to database: " + e.getMessage(), e);
@@ -312,7 +283,8 @@ public class SubsetEndpoint extends RestEndpoint {
                             @Header(name = "Access-Control-Expose-Headers", description = "Reverse proxy exposing of custom headers", schema = @Schema(implementation = String.class), required = true)},
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = List.class))}),
+                            schema = @Schema(implementation = List.class)),
+                            @Content(mediaType = "text/csv")}),
             @ApiResponse(responseCode = "400",
                     description = "Invalid pagination",
                     content = {@Content(
@@ -334,18 +306,20 @@ public class SubsetEndpoint extends RestEndpoint {
                             mediaType = "application/json",
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
-    public ResponseEntity<List<Map<String, Object>>> getData(@NotNull @PathVariable("databaseId") UUID databaseId,
-                                                             @NotNull @PathVariable("subsetId") UUID subsetId,
-                                                             Principal principal,
-                                                             @NotNull HttpServletRequest request,
-                                                             @RequestParam(required = false) Instant timestamp,
-                                                             @RequestParam(required = false) Long page,
-                                                             @RequestParam(required = false) Long size)
+    public ResponseEntity<?> getData(@NotNull @PathVariable("databaseId") UUID databaseId,
+                                     @NotNull @PathVariable("subsetId") UUID subsetId,
+                                     Principal principal,
+                                     @NotNull @RequestHeader("Accept") String accept,
+                                     @NotNull HttpServletRequest request,
+                                     @RequestParam(required = false) Instant timestamp,
+                                     @RequestParam(required = false) Long page,
+                                     @RequestParam(required = false) Long size)
             throws PaginationException, DatabaseNotFoundException, RemoteUnavailableException, NotAllowedException,
             QueryNotFoundException, DatabaseUnavailableException, QueryMalformedException, UserNotFoundException,
-            MetadataServiceException, TableNotFoundException, ViewNotFoundException, ViewMalformedException {
-        log.debug("endpoint get subset data, databaseId={}, subsetId={}, principal.name={} page={}, size={}",
-                databaseId, subsetId, principal != null ? principal.getName() : null, page, size);
+            MetadataServiceException, TableNotFoundException, ViewNotFoundException, ViewMalformedException,
+            FormatNotAvailableException, StorageUnavailableException {
+        log.debug("endpoint get subset data, databaseId={}, subsetId={}, accept={} page={}, size={}", databaseId,
+                subsetId, accept, page, size);
         endpointValidator.validateDataParams(page, size);
         final DatabaseDto database = cacheService.getDatabase(databaseId);
         if (!database.getIsPublic()) {
@@ -353,7 +327,9 @@ public class SubsetEndpoint extends RestEndpoint {
                 log.error("Failed to re-execute query: no authentication found");
                 throw new NotAllowedException("Failed to re-execute query: no authentication found");
             }
-            cacheService.getAccess(databaseId, getId(principal));
+            if (!isSystem(principal)) {
+                cacheService.getAccess(databaseId, getId(principal));
+            }
         }
         log.trace("visibility for database: is_public={}, is_schema_public={}", database.getIsPublic(), database.getIsSchemaPublic());
         /* parameters */
@@ -369,6 +345,10 @@ public class SubsetEndpoint extends RestEndpoint {
             timestamp = Instant.now();
             log.debug("timestamp not set: default to {}", timestamp);
         }
+        if (accept == null || accept.isBlank()) {
+            accept = MediaType.APPLICATION_JSON_VALUE;
+            log.debug("accept header not set: default to {}", accept);
+        }
         try {
             final HttpHeaders headers = new HttpHeaders();
             headers.set("X-Id", "" + subsetId);
@@ -382,16 +362,32 @@ public class SubsetEndpoint extends RestEndpoint {
                         .build();
             }
             subset.setIdentifiers(metadataServiceGateway.getIdentifiers(database.getId(), subset.getId()));
-            final String query = mariaDbMapper.rawSelectQuery(subset.getQuery(), timestamp, page, size);
+            final String query = mariaDbMapper.rawSelectQuery(subset.getQuery(), timestamp,
+                    accept.equals("text/csv") ? null : page,
+                    accept.equals("text/csv") ? null : size);
             final Dataset<Row> dataset = subsetService.getData(database, query);
-            final String viewName = metadataMapper.queryDtoToViewName(subset);
+            final String viewName = subset.getQueryHash();
             databaseService.createView(database, viewName, subset.getQuery());
             final ViewDto view = databaseService.inspectView(database, viewName);
             headers.set("Access-Control-Expose-Headers", "X-Id X-Headers");
             headers.set("X-Headers", String.join(",", view.getColumns().stream().map(ViewColumnDto::getInternalName).toList()));
-            return ResponseEntity.status(request.getMethod().equals("POST") ? HttpStatus.CREATED : HttpStatus.OK)
-                    .headers(headers)
-                    .body(transform(dataset));
+            final HttpStatusCode statusCode = request.getMethod().equals("POST") ? HttpStatus.CREATED : HttpStatus.OK;
+            switch (accept) {
+                case MediaType.APPLICATION_JSON_VALUE:
+                    log.trace("accept header matches json");
+                    return ResponseEntity.status(statusCode)
+                            .headers(headers)
+                            .body(transform(dataset));
+                case "text/csv":
+                    log.trace("accept header matches csv");
+                    final ExportResourceDto resource = storageService.transformDataset(dataset);
+                    headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\"");
+                    return ResponseEntity.status(statusCode)
+                            .headers(headers)
+                            .body(storageService.transformDataset(dataset)
+                                    .getResource());
+            }
+            throw new FormatNotAvailableException("Must provide either application/json or text/csv value for header 'Accept': provided " + accept + " instead");
         } catch (SQLException e) {
             log.error("Failed to establish connection to database: {}", e.getMessage());
             throw new DatabaseUnavailableException("Failed to establish connection to database: " + e.getMessage(), e);
diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
index 6914726cb84a886d97210dce9f1f60ac19db53b5..00dc146332f4eb0daf3f2a8cacde65c1d2535fec 100644
--- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
+++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
@@ -29,9 +29,9 @@ import lombok.extern.log4j.Log4j2;
 import org.apache.spark.sql.Dataset;
 import org.apache.spark.sql.Row;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.io.InputStreamResource;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
@@ -40,7 +40,6 @@ import java.security.Principal;
 import java.sql.SQLException;
 import java.time.Instant;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 @Log4j2
@@ -228,7 +227,8 @@ public class TableEndpoint extends RestEndpoint {
                             @Header(name = "Access-Control-Expose-Headers", description = "Expose `X-Count` custom header", schema = @Schema(implementation = String.class), required = true)},
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = List.class))}),
+                            schema = @Schema(implementation = List.class)),
+                            @Content(mediaType = "text/csv")}),
             @ApiResponse(responseCode = "400",
                     description = "Request pagination or table data select query is malformed",
                     content = {@Content(
@@ -250,17 +250,19 @@ public class TableEndpoint extends RestEndpoint {
                             mediaType = "application/json",
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
-    public ResponseEntity<List<Map<String, Object>>> getData(@NotNull @PathVariable("databaseId") UUID databaseId,
-                                                             @NotNull @PathVariable("tableId") UUID tableId,
-                                                             @RequestParam(required = false) Instant timestamp,
-                                                             @RequestParam(required = false) Long page,
-                                                             @RequestParam(required = false) Long size,
-                                                             @NotNull HttpServletRequest request,
-                                                             Principal principal)
+    public ResponseEntity<?> getData(@NotNull @PathVariable("databaseId") UUID databaseId,
+                                     @NotNull @PathVariable("tableId") UUID tableId,
+                                     @RequestParam(required = false) Instant timestamp,
+                                     @RequestParam(required = false) Long page,
+                                     @RequestParam(required = false) Long size,
+                                     @NotNull @RequestHeader("Accept") String accept,
+                                     @NotNull HttpServletRequest request,
+                                     Principal principal)
             throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException,
-            PaginationException, MetadataServiceException, NotAllowedException, DatabaseNotFoundException {
-        log.debug("endpoint get table data, databaseId={}, tableId={}, timestamp={}, page={}, size={}", databaseId,
-                tableId, timestamp, page, size);
+            PaginationException, MetadataServiceException, NotAllowedException, DatabaseNotFoundException,
+            FormatNotAvailableException, StorageUnavailableException {
+        log.debug("endpoint get table data, databaseId={}, tableId={}, timestamp={}, page={}, size={}, accept={}",
+                databaseId, tableId, timestamp, page, size, accept);
         endpointValidator.validateDataParams(page, size);
         /* parameters */
         if (page == null) {
@@ -281,7 +283,9 @@ public class TableEndpoint extends RestEndpoint {
                 log.error("Failed find table data: authentication required");
                 throw new NotAllowedException("Failed to find table data: authentication required");
             }
-            cacheService.getAccess(databaseId, getId(principal));
+            if (!isSystem(principal)) {
+                cacheService.getAccess(databaseId, getId(principal));
+            }
         }
         final DatabaseDto database = cacheService.getDatabase(databaseId);
         try {
@@ -296,11 +300,26 @@ public class TableEndpoint extends RestEndpoint {
             headers.set("Access-Control-Expose-Headers", "X-Headers");
             headers.set("X-Headers", String.join(",", table.getColumns().stream().map(ColumnDto::getInternalName).toList()));
             final String query = mariaDbMapper.defaultRawSelectQuery(database.getInternalName(),
-                    table.getInternalName(), timestamp, page, size);
+                    table.getInternalName(), timestamp,
+                    accept.equals("text/csv") ? null : page,
+                    accept.equals("text/csv") ? null : size);
             final Dataset<Row> dataset = subsetService.getData(database, query);
-            return ResponseEntity.ok()
-                    .headers(headers)
-                    .body(transform(dataset));
+            switch (accept) {
+                case MediaType.APPLICATION_JSON_VALUE:
+                    log.trace("accept header matches json");
+                    return ResponseEntity.ok()
+                            .headers(headers)
+                            .body(transform(dataset));
+                case "text/csv":
+                    log.trace("accept header matches csv");
+                    final ExportResourceDto resource = storageService.transformDataset(dataset);
+                    headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\"");
+                    return ResponseEntity.status(HttpStatus.OK)
+                            .headers(headers)
+                            .body(storageService.transformDataset(dataset)
+                                    .getResource());
+            }
+            throw new FormatNotAvailableException("Must provide either application/json or text/csv value for header 'Accept': provided " + accept + " instead");
         } catch (SQLException | QueryMalformedException e) {
             log.error("Failed to establish connection to database: {}", e.getMessage());
             throw new DatabaseUnavailableException("Failed to establish connection to database: " + e.getMessage(), e);
@@ -586,72 +605,6 @@ public class TableEndpoint extends RestEndpoint {
         }
     }
 
-    @GetMapping("/{tableId}/export")
-    @Observed(name = "dbrepo_table_data_export")
-    @Operation(summary = "Get table data",
-            description = "Gets data from table with id as downloadable file. For tables in private databases, the user needs to have at least *READ* access to the associated database.",
-            security = {@SecurityRequirement(name = "basicAuth"), @SecurityRequirement(name = "bearerAuth")})
-    @ApiResponses(value = {
-            @ApiResponse(responseCode = "200",
-                    description = "Exported table data",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = InputStreamResource.class))}),
-            @ApiResponse(responseCode = "400",
-                    description = "Request pagination or table data select query is malformed",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "403",
-                    description = "Export table data not allowed",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "404",
-                    description = "Failed to find table in metadata database",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "503",
-                    description = "Failed to establish connection with the metadata service",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-    })
-    public ResponseEntity<InputStreamResource> exportDataset(@NotNull @PathVariable("databaseId") UUID databaseId,
-                                                             @NotNull @PathVariable("tableId") UUID tableId,
-                                                             @RequestParam(required = false) Instant timestamp,
-                                                             Principal principal)
-            throws RemoteUnavailableException, TableNotFoundException, NotAllowedException, StorageUnavailableException,
-            QueryMalformedException, MetadataServiceException, DatabaseNotFoundException {
-        log.debug("endpoint export table data, databaseId={}, tableId={}, timestamp={}", databaseId, tableId, timestamp);
-        /* parameters */
-        if (timestamp == null) {
-            timestamp = Instant.now();
-            log.debug("timestamp not set: default to {}", timestamp);
-        }
-        final TableDto table = cacheService.getTable(databaseId, tableId);
-        if (!table.getIsPublic()) {
-            if (principal == null) {
-                log.error("Failed to export private table: principal is null");
-                throw new NotAllowedException("Failed to export private table: principal is null");
-            }
-            cacheService.getAccess(databaseId, getId(principal));
-        }
-        final DatabaseDto database = cacheService.getDatabase(databaseId);
-        final String query = mariaDbMapper.defaultRawSelectQuery(database.getInternalName(),
-                table.getInternalName(), timestamp, null, null);
-        final Dataset<Row> dataset = subsetService.getData(cacheService.getDatabase(table.getDatabaseId()),
-                query);
-        final ExportResourceDto resource = storageService.transformDataset(dataset);
-        final HttpHeaders headers = new HttpHeaders();
-        headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\"");
-        log.trace("export table resulted in resource {}", resource);
-        return ResponseEntity.ok()
-                .headers(headers)
-                .body(resource.getResource());
-    }
-
     @PostMapping("/{tableId}/data/import")
     @Observed(name = "dbrepo_table_data_import")
     @PreAuthorize("hasAuthority('insert-table-data')")
diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java
index 5c1fdb57dbc30b428d104e4c145f4a0064105f78..3a06b2fe7c4148ef4a53dcb9626de125aaf619e1 100644
--- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java
+++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java
@@ -25,9 +25,9 @@ import lombok.extern.log4j.Log4j2;
 import org.apache.spark.sql.Dataset;
 import org.apache.spark.sql.Row;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.io.InputStreamResource;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
@@ -36,7 +36,6 @@ import java.security.Principal;
 import java.sql.SQLException;
 import java.time.Instant;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 @Log4j2
@@ -225,7 +224,8 @@ public class ViewEndpoint extends RestEndpoint {
                             @Header(name = "Access-Control-Expose-Headers", description = "Expose `X-Count` custom header", schema = @Schema(implementation = String.class), required = true)},
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = List.class))}),
+                            schema = @Schema(implementation = List.class)),
+                            @Content(mediaType = "text/csv")}),
             @ApiResponse(responseCode = "400",
                     description = "Request pagination is malformed",
                     content = {@Content(
@@ -252,17 +252,20 @@ public class ViewEndpoint extends RestEndpoint {
                             mediaType = "application/json",
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
-    public ResponseEntity<List<Map<String, Object>>> getData(@NotNull @PathVariable("databaseId") UUID databaseId,
-                                                             @NotNull @PathVariable("viewId") UUID viewId,
-                                                             @RequestParam(required = false) Long page,
-                                                             @RequestParam(required = false) Long size,
-                                                             @RequestParam(required = false) Instant timestamp,
-                                                             @NotNull HttpServletRequest request,
-                                                             Principal principal)
+    public ResponseEntity<?> getData(@NotNull @PathVariable("databaseId") UUID databaseId,
+                                     @NotNull @PathVariable("viewId") UUID viewId,
+                                     @RequestParam(required = false) Long page,
+                                     @RequestParam(required = false) Long size,
+                                     @RequestParam(required = false) Instant timestamp,
+                                     @NotNull HttpServletRequest request,
+                                     @NotNull @RequestHeader("Accept") String accept,
+                                     Principal principal)
             throws DatabaseUnavailableException, RemoteUnavailableException, ViewNotFoundException, PaginationException,
-            QueryMalformedException, NotAllowedException, MetadataServiceException, TableNotFoundException, DatabaseNotFoundException {
-        log.debug("endpoint get view data, databaseId={}, viewId={}, page={}, size={}, timestamp={}", databaseId,
-                viewId, page, size, timestamp);
+            QueryMalformedException, NotAllowedException, MetadataServiceException, TableNotFoundException,
+            DatabaseNotFoundException, ViewMalformedException, StorageUnavailableException,
+            FormatNotAvailableException {
+        log.debug("endpoint get view data, databaseId={}, viewId={}, page={}, size={}, accept={}, timestamp={}",
+                databaseId, viewId, page, size, accept, timestamp);
         endpointValidator.validateDataParams(page, size);
         /* parameters */
         if (page == null) {
@@ -283,7 +286,9 @@ public class ViewEndpoint extends RestEndpoint {
                 log.error("Failed to get data from view: unauthorized");
                 throw new NotAllowedException("Failed to get data from view: unauthorized");
             }
-            cacheService.getAccess(databaseId, getId(principal));
+            if (!isSystem(principal)) {
+                cacheService.getAccess(databaseId, getId(principal));
+            }
         }
         final DatabaseDto database = cacheService.getDatabase(databaseId);
         try {
@@ -297,84 +302,32 @@ public class ViewEndpoint extends RestEndpoint {
             }
             headers.set("Access-Control-Expose-Headers", "X-Headers");
             headers.set("X-Headers", String.join(",", view.getColumns().stream().map(ViewColumnDto::getInternalName).toList()));
-            final String query = mariaDbMapper.defaultRawSelectQuery(database.getInternalName(),
-                    view.getInternalName(), timestamp, page, size);
-            final Dataset<Row> dataset = subsetService.getData(cacheService.getDatabase(databaseId),
-                    query);
-            return ResponseEntity.ok()
-                    .headers(headers)
-                    .body(transform(dataset));
+            final String query = mariaDbMapper.rawSelectQuery(view.getQuery(), timestamp,
+                    accept.equals("text/csv") ? null : page,
+                    accept.equals("text/csv") ? null : size);
+            final Dataset<Row> dataset = subsetService.getData(database, query);
+            final String viewName = view.getQueryHash();
+            databaseService.createView(database, viewName, view.getQuery());
+            switch (accept) {
+                case MediaType.APPLICATION_JSON_VALUE:
+                    log.trace("accept header matches json");
+                    return ResponseEntity.ok()
+                            .headers(headers)
+                            .body(transform(dataset));
+                case "text/csv":
+                    log.trace("accept header matches csv");
+                    final ExportResourceDto resource = storageService.transformDataset(dataset);
+                    headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\"");
+                    return ResponseEntity.ok()
+                            .headers(headers)
+                            .body(storageService.transformDataset(dataset)
+                                    .getResource());
+            }
+            throw new FormatNotAvailableException("Must provide either application/json or text/csv value for header 'Accept': provided " + accept + " instead");
         } catch (SQLException e) {
             log.error("Failed to establish connection to database: {}", e.getMessage());
             throw new DatabaseUnavailableException("Failed to establish connection to database: " + e.getMessage(), e);
         }
     }
 
-    @GetMapping("/{viewId}/export")
-    @Observed(name = "dbrepo_view_data_export")
-    @Operation(summary = "Get view data",
-            description = "Gets data from view with id as downloadable file. For tables in private databases, the user needs to have at least *READ* access to the associated database.",
-            security = {@SecurityRequirement(name = "basicAuth"), @SecurityRequirement(name = "bearerAuth")})
-    @ApiResponses(value = {
-            @ApiResponse(responseCode = "200",
-                    description = "Exported view data",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = InputStreamResource.class))}),
-            @ApiResponse(responseCode = "400",
-                    description = "Request pagination or view data select query is malformed",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "403",
-                    description = "Export view data not allowed",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "404",
-                    description = "Failed to find view in metadata database or export dataset",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "503",
-                    description = "Failed to establish connection with the metadata service",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-    })
-    public ResponseEntity<InputStreamResource> exportDataset(@NotNull @PathVariable("databaseId") UUID databaseId,
-                                                             @NotNull @PathVariable("viewId") UUID viewId,
-                                                             @RequestParam(required = false) Instant timestamp,
-                                                             Principal principal)
-            throws RemoteUnavailableException, ViewNotFoundException, NotAllowedException, MetadataServiceException,
-            StorageUnavailableException, QueryMalformedException, TableNotFoundException, DatabaseNotFoundException {
-        log.debug("endpoint export view data, databaseId={}, viewId={}", databaseId, viewId);
-        /* parameters */
-        if (timestamp == null) {
-            timestamp = Instant.now();
-            log.debug("timestamp not set: default to {}", timestamp);
-        }
-        /* parameters */
-        final ViewDto view = cacheService.getView(databaseId, viewId);
-        if (!view.getIsPublic()) {
-            if (principal == null) {
-                log.error("Failed to export private view: principal is null");
-                throw new NotAllowedException("Failed to export private view: principal is null");
-            }
-            cacheService.getAccess(databaseId, getId(principal));
-        }
-        final DatabaseDto database = cacheService.getDatabase(databaseId);
-        final String query = mariaDbMapper.defaultRawSelectQuery(database.getInternalName(),
-                view.getInternalName(), timestamp, null, null);
-        final Dataset<Row> dataset = subsetService.getData(cacheService.getDatabase(databaseId),
-                query);
-        final ExportResourceDto resource = storageService.transformDataset(dataset);
-        final HttpHeaders headers = new HttpHeaders();
-        headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\"");
-        log.trace("export table resulted in resource {}", resource);
-        return ResponseEntity.ok()
-                .headers(headers)
-                .body(resource.getResource());
-    }
-
 }
diff --git a/dbrepo-data-service/rest-service/src/main/resources/application.yml b/dbrepo-data-service/rest-service/src/main/resources/application.yml
index b5f592d570cb73677baaa00666af945901e1d256..53c0858bbd98acd3bb250b0def751534bc920548 100644
--- a/dbrepo-data-service/rest-service/src/main/resources/application.yml
+++ b/dbrepo-data-service/rest-service/src/main/resources/application.yml
@@ -74,6 +74,7 @@ dbrepo:
     default:
       read: "${GRANT_DEFAULT_READ:SELECT}"
       write: "${GRANT_DEFAULT_WRITE:SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE}"
+  website: "${BASE_URL:http://localhost}"
   credentialCacheTimeout: "${CREDENTIAL_CACHE_TIMEOUT:60}"
   minConcurrent: "${MIN_CONCURRENT_CONSUMERS:2}"
   maxConcurrent: "${MAX_CONCURRENT_CONSUMERS:6}"
diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java
index 9cdb0d99c82e399c1d8297c1c71dd1f6dd963a31..ca97569f3c00b54b466af97cd99d44aea4f31d0e 100644
--- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java
+++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java
@@ -33,7 +33,6 @@ import java.security.Principal;
 import java.sql.SQLException;
 import java.time.Instant;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 import static org.junit.jupiter.api.Assertions.*;
@@ -145,16 +144,15 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            generic_findById(DATABASE_1_ID, QUERY_1_ID, "application/json", null, null);
+            generic_findById(DATABASE_1_ID, QUERY_1_ID, null, null);
         });
     }
 
     @Test
     @WithMockUser(username = USER_1_USERNAME)
     public void findById_privateDataPrivateSchema_succeeds() throws DatabaseNotFoundException, SQLException,
-            RemoteUnavailableException, UserNotFoundException, QueryNotFoundException, MetadataServiceException,
-            DatabaseUnavailableException, TableNotFoundException, StorageUnavailableException, NotAllowedException,
-            QueryMalformedException, FormatNotAvailableException {
+            RemoteUnavailableException, UserNotFoundException, DatabaseUnavailableException, NotAllowedException,
+            QueryNotFoundException, MetadataServiceException {
 
         /* mock */
         when(credentialService.getDatabase(DATABASE_1_ID))
@@ -163,15 +161,14 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(QUERY_1_DTO);
 
         /* test */
-        generic_findById(DATABASE_1_ID, QUERY_1_ID, "application/json", null, USER_1_PRINCIPAL);
+        generic_findById(DATABASE_1_ID, QUERY_1_ID, null, USER_1_PRINCIPAL);
     }
 
     @Test
     @WithMockUser(username = USER_1_USERNAME)
     public void findById_privateDataPrivateSchemaAcceptEmpty_succeeds() throws DatabaseNotFoundException, SQLException,
-            RemoteUnavailableException, UserNotFoundException, QueryNotFoundException, MetadataServiceException,
-            DatabaseUnavailableException, TableNotFoundException, StorageUnavailableException, NotAllowedException,
-            QueryMalformedException, FormatNotAvailableException {
+            RemoteUnavailableException, UserNotFoundException, DatabaseUnavailableException, NotAllowedException,
+            QueryNotFoundException, MetadataServiceException {
 
         /* mock */
         when(credentialService.getDatabase(DATABASE_1_ID))
@@ -180,15 +177,14 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(QUERY_1_DTO);
 
         /* test */
-        generic_findById(DATABASE_1_ID, QUERY_1_ID, null, null, USER_1_PRINCIPAL);
+        generic_findById(DATABASE_1_ID, QUERY_1_ID, null, USER_1_PRINCIPAL);
     }
 
     @Test
     @WithMockUser(username = USER_3_USERNAME)
     public void findById_publicDataPrivateSchema_succeeds() throws DatabaseNotFoundException, SQLException,
             RemoteUnavailableException, UserNotFoundException, DatabaseUnavailableException, NotAllowedException,
-            StorageUnavailableException, QueryMalformedException, QueryNotFoundException,
-            FormatNotAvailableException, TableNotFoundException, MetadataServiceException {
+            QueryNotFoundException, MetadataServiceException {
 
         /* mock */
         when(credentialService.getDatabase(DATABASE_3_ID))
@@ -197,7 +193,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(QUERY_5_DTO);
 
         /* test */
-        generic_findById(DATABASE_3_ID, QUERY_5_ID, "application/json", null, USER_3_PRINCIPAL);
+        generic_findById(DATABASE_3_ID, QUERY_5_ID, null, USER_3_PRINCIPAL);
     }
 
     @Test
@@ -213,16 +209,16 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(FormatNotAvailableException.class, () -> {
-            generic_findById(DATABASE_4_ID, QUERY_7_ID, "application/pdf", null, null);
+            generic_findById(DATABASE_4_ID, QUERY_7_ID, null, null);
         });
     }
 
     @Test
     @WithMockUser(username = USER_1_USERNAME)
-    public void findById_privateDataPrivateSchemaAcceptCsv_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException,
-            UserNotFoundException, DatabaseUnavailableException, StorageUnavailableException, QueryMalformedException,
-            QueryNotFoundException, FormatNotAvailableException, SQLException, MetadataServiceException,
-            TableNotFoundException, NotAllowedException {
+    public void findById_privateDataPrivateSchemaAcceptCsv_succeeds() throws DatabaseNotFoundException,
+            RemoteUnavailableException, UserNotFoundException, DatabaseUnavailableException,
+            StorageUnavailableException, QueryMalformedException, QueryNotFoundException, SQLException,
+            MetadataServiceException, TableNotFoundException, NotAllowedException {
         final Dataset<Row> mock = sparkSession.emptyDataFrame();
 
         /* mock */
@@ -236,7 +232,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(EXPORT_RESOURCE_DTO);
 
         /* test */
-        generic_findById(DATABASE_1_ID, QUERY_1_ID, "text/csv", null, USER_1_PRINCIPAL);
+        generic_findById(DATABASE_1_ID, QUERY_1_ID, null, USER_1_PRINCIPAL);
     }
 
     @Test
@@ -250,7 +246,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            generic_findById(DATABASE_3_ID, QUERY_5_ID, "text/csv", Instant.now(), null);
+            generic_findById(DATABASE_3_ID, QUERY_5_ID, Instant.now(), null);
         });
     }
 
@@ -273,7 +269,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            generic_findById(DATABASE_3_ID, QUERY_5_ID, "text/csv", Instant.now(), null);
+            generic_findById(DATABASE_3_ID, QUERY_5_ID, Instant.now(), null);
         });
     }
 
@@ -289,7 +285,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(DatabaseNotFoundException.class, () -> {
-            generic_findById(DATABASE_3_ID, QUERY_5_ID, "application/json", null, null);
+            generic_findById(DATABASE_3_ID, QUERY_5_ID, null, null);
         });
     }
 
@@ -307,7 +303,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(DatabaseUnavailableException.class, () -> {
-            generic_findById(DATABASE_3_ID, QUERY_5_ID, "application/json", null, USER_3_PRINCIPAL);
+            generic_findById(DATABASE_3_ID, QUERY_5_ID, null, USER_3_PRINCIPAL);
         });
     }
 
@@ -318,7 +314,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
             StorageNotFoundException, DatabaseUnavailableException, StorageUnavailableException, SQLException,
             QueryMalformedException, QueryNotFoundException, DatabaseNotFoundException, RemoteUnavailableException,
             MetadataServiceException, TableNotFoundException, ViewMalformedException, ViewNotFoundException,
-            ImageNotFoundException {
+            ImageNotFoundException, FormatNotAvailableException {
         final Dataset<Row> mock = sparkSession.emptyDataFrame();
 
         /* mock */
@@ -348,7 +344,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
             PaginationException, StorageNotFoundException, DatabaseUnavailableException, StorageUnavailableException,
             QueryMalformedException, QueryNotFoundException, DatabaseNotFoundException, RemoteUnavailableException,
             SQLException, MetadataServiceException, TableNotFoundException, ViewMalformedException,
-            ViewNotFoundException, ImageNotFoundException {
+            ViewNotFoundException, ImageNotFoundException, FormatNotAvailableException {
         final Dataset<Row> mock = sparkSession.emptyDataFrame();
 
         /* mock */
@@ -398,7 +394,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
             TableMalformedException, NotAllowedException, SQLException, QueryNotFoundException, PaginationException,
             DatabaseUnavailableException, StorageUnavailableException, QueryMalformedException,
             QueryNotSupportedException, StorageNotFoundException, TableNotFoundException, ViewMalformedException,
-            ViewNotFoundException, ImageNotFoundException {
+            ViewNotFoundException, ImageNotFoundException, FormatNotAvailableException {
         final Dataset<Row> mock = sparkSession.emptyDataFrame();
 
         /* mock */
@@ -426,7 +422,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
             NotAllowedException, SQLException, QueryNotFoundException, DatabaseUnavailableException,
             StorageUnavailableException, QueryMalformedException, QueryNotSupportedException, PaginationException,
             StorageNotFoundException, TableNotFoundException, ViewMalformedException, ViewNotFoundException,
-            ImageNotFoundException {
+            ImageNotFoundException, FormatNotAvailableException {
         final Dataset<Row> mock = sparkSession.emptyDataFrame();
 
         /* mock */
@@ -478,7 +474,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
     public void getData_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException,
             NotAllowedException, SQLException, QueryNotFoundException, QueryMalformedException,
             DatabaseUnavailableException, PaginationException, MetadataServiceException, TableNotFoundException,
-            ViewNotFoundException, ViewMalformedException {
+            ViewNotFoundException, ViewMalformedException, StorageUnavailableException, FormatNotAvailableException {
         final Dataset<Row> mock = sparkSession.emptyDataFrame();
 
         /* mock */
@@ -498,16 +494,17 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn("GET");
 
         /* test */
-        final ResponseEntity<List<Map<String, Object>>> response = subsetEndpoint.getData(DATABASE_3_ID, QUERY_5_ID, null, httpServletRequest, null, null, null);
+        final ResponseEntity<?> response = subsetEndpoint.getData(DATABASE_3_ID, QUERY_5_ID,
+                null, "GET", httpServletRequest, null, null, null);
         assertEquals(HttpStatus.OK, response.getStatusCode());
         assertNotNull(response.getBody());
     }
 
     @Test
     public void getData_head_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException,
-            UserNotFoundException, NotAllowedException, SQLException, QueryNotFoundException, TableMalformedException,
-            QueryMalformedException, DatabaseUnavailableException, PaginationException, MetadataServiceException,
-            TableNotFoundException, ViewNotFoundException, ViewMalformedException {
+            UserNotFoundException, NotAllowedException, SQLException, QueryNotFoundException, QueryMalformedException,
+            DatabaseUnavailableException, PaginationException, MetadataServiceException, TableNotFoundException,
+            ViewNotFoundException, ViewMalformedException, StorageUnavailableException, FormatNotAvailableException {
 
         /* mock */
         when(credentialService.getDatabase(DATABASE_3_ID))
@@ -520,7 +517,8 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn("HEAD");
 
         /* test */
-        final ResponseEntity<List<Map<String, Object>>> response = subsetEndpoint.getData(DATABASE_3_ID, QUERY_5_ID, null, httpServletRequest, null, null, null);
+        final ResponseEntity<?> response = subsetEndpoint.getData(DATABASE_3_ID, QUERY_5_ID,
+                null, "GET", httpServletRequest, null, null, null);
         assertEquals(HttpStatus.OK, response.getStatusCode());
         assertNotNull(response.getHeaders().get("X-Count"));
         assertEquals(1, response.getHeaders().get("X-Count").size());
@@ -532,7 +530,8 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
     public void getData_private_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException,
             UserNotFoundException, DatabaseUnavailableException, NotAllowedException, QueryMalformedException,
             QueryNotFoundException, PaginationException, SQLException, MetadataServiceException,
-            TableNotFoundException, ViewNotFoundException, ViewMalformedException {
+            TableNotFoundException, ViewNotFoundException, ViewMalformedException, StorageUnavailableException,
+            FormatNotAvailableException {
         final Dataset<Row> mock = sparkSession.emptyDataFrame();
 
         /* mock */
@@ -552,7 +551,8 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn("GET");
 
         /* test */
-        final ResponseEntity<List<Map<String, Object>>> response = subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, null, null);
+        final ResponseEntity<?> response = subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID,
+                USER_1_PRINCIPAL, "GET", httpServletRequest, null, null, null);
         assertEquals(HttpStatus.OK, response.getStatusCode());
         assertNotNull(response.getBody());
     }
@@ -568,7 +568,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, null, httpServletRequest, null, null, null);
+            subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, null, "GET", httpServletRequest, null, null, null);
         });
     }
 
@@ -586,7 +586,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, null, null);
+            subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, "GET", httpServletRequest, null, null, null);
         });
     }
 
@@ -595,7 +595,8 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
     public void getData_privateHead_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException,
             UserNotFoundException, DatabaseUnavailableException, NotAllowedException, QueryMalformedException,
             QueryNotFoundException, PaginationException, SQLException, MetadataServiceException,
-            TableNotFoundException, ViewNotFoundException, ViewMalformedException {
+            TableNotFoundException, ViewNotFoundException, ViewMalformedException, StorageUnavailableException,
+            FormatNotAvailableException {
 
         /* mock */
         when(credentialService.getDatabase(DATABASE_1_ID))
@@ -608,7 +609,8 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn("HEAD");
 
         /* test */
-        final ResponseEntity<List<Map<String, Object>>> response = subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, null, null);
+        final ResponseEntity<?> response = subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID,
+                USER_1_PRINCIPAL, "GET", httpServletRequest, null, null, null);
         assertEquals(HttpStatus.OK, response.getStatusCode());
         assertNotNull(response.getHeaders().get("X-Count"));
         assertEquals(1, response.getHeaders().get("X-Count").size());
@@ -734,14 +736,12 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
         return response.getBody();
     }
 
-    protected void generic_findById(UUID databaseId, UUID subsetId, String accept, Instant timestamp,
-                                    Principal principal) throws UserNotFoundException, DatabaseUnavailableException,
-            StorageUnavailableException, NotAllowedException, QueryMalformedException, QueryNotFoundException,
-            DatabaseNotFoundException, RemoteUnavailableException, FormatNotAvailableException,
-            MetadataServiceException, TableNotFoundException {
+    protected void generic_findById(UUID databaseId, UUID subsetId, Instant timestamp, Principal principal)
+            throws UserNotFoundException, DatabaseUnavailableException, NotAllowedException, QueryNotFoundException,
+            DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException {
 
         /* test */
-        final ResponseEntity<?> response = subsetEndpoint.findById(databaseId, subsetId, accept, timestamp, principal);
+        final ResponseEntity<?> response = subsetEndpoint.findById(databaseId, subsetId, timestamp, principal);
         assertEquals(HttpStatus.OK, response.getStatusCode());
         assertNotNull(response.getBody());
     }
diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java
index 25f3b60ceac1f47225e73c237cefc215c21f1696..3a5a62731bc595c2e971a382cf6c3a1c1b5791aa 100644
--- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java
+++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java
@@ -39,7 +39,6 @@ import java.sql.SQLException;
 import java.time.Instant;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.stream.Stream;
 
 import static org.junit.jupiter.api.Assertions.*;
@@ -292,7 +291,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
     @WithAnonymousUser
     public void getData_publicDataPrivateSchema_succeeds() throws DatabaseUnavailableException, TableNotFoundException, QueryMalformedException,
             RemoteUnavailableException, PaginationException, MetadataServiceException, NotAllowedException,
-            DatabaseNotFoundException {
+            DatabaseNotFoundException, StorageUnavailableException, FormatNotAvailableException {
         final Dataset<Row> mock = sparkSession.emptyDataFrame();
 
         /* mock */
@@ -306,7 +305,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn("GET");
 
         /* test */
-        final ResponseEntity<List<Map<String, Object>>> response = tableEndpoint.getData(DATABASE_1_ID, TABLE_4_ID, null, null, null, httpServletRequest, null);
+        final ResponseEntity<?> response = tableEndpoint.getData(DATABASE_1_ID, TABLE_4_ID, null, null, null, "application/json", httpServletRequest, null);
         assertEquals(HttpStatus.OK, response.getStatusCode());
 
     }
@@ -315,7 +314,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
     @WithAnonymousUser
     public void getData_head_succeeds() throws DatabaseUnavailableException, TableNotFoundException,
             SQLException, QueryMalformedException, RemoteUnavailableException, PaginationException,
-            MetadataServiceException, NotAllowedException, DatabaseNotFoundException {
+            MetadataServiceException, NotAllowedException, DatabaseNotFoundException, StorageUnavailableException, FormatNotAvailableException {
         final Dataset<Row> mock = sparkSession.emptyDataFrame();
 
         /* mock */
@@ -331,7 +330,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn("HEAD");
 
         /* test */
-        final ResponseEntity<List<Map<String, Object>>> response = tableEndpoint.getData(DATABASE_2_ID, TABLE_5_ID, null, null, null, httpServletRequest, null);
+        final ResponseEntity<?> response = tableEndpoint.getData(DATABASE_2_ID, TABLE_5_ID, null, null, null, "application/json", httpServletRequest, null);
         assertEquals(HttpStatus.OK, response.getStatusCode());
         assertNotNull(response.getHeaders().get("Access-Control-Expose-Headers"));
         assertEquals("X-Count", response.getHeaders().get("Access-Control-Expose-Headers").get(0));
@@ -351,7 +350,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, httpServletRequest, null);
+            tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, "application/json", httpServletRequest, null);
         });
     }
 
@@ -369,14 +368,14 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, httpServletRequest, USER_2_PRINCIPAL);
+            tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, "application/json", httpServletRequest, USER_2_PRINCIPAL);
         });
     }
 
     @Test
     @WithAnonymousUser
     public void getData_notAllowed_fails() throws TableNotFoundException, RemoteUnavailableException,
-            MetadataServiceException{
+            MetadataServiceException {
 
         /* mock */
         when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID))
@@ -386,7 +385,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            tableEndpoint.getData(DATABASE_3_ID, TABLE_8_ID, null, null, null, httpServletRequest, null);
+            tableEndpoint.getData(DATABASE_3_ID, TABLE_8_ID, null, null, null, "application/json", httpServletRequest, null);
         });
     }
 
@@ -408,7 +407,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(DatabaseUnavailableException.class, () -> {
-            tableEndpoint.getData(DATABASE_2_ID, TABLE_5_ID, null, null, null, httpServletRequest, null);
+            tableEndpoint.getData(DATABASE_2_ID, TABLE_5_ID, null, null, null, "application/json", httpServletRequest, null);
         });
     }
 
@@ -426,7 +425,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(RemoteUnavailableException.class, () -> {
-            tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, httpServletRequest, USER_2_PRINCIPAL);
+            tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, "application/json", httpServletRequest, USER_2_PRINCIPAL);
         });
     }
 
@@ -435,7 +434,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
     @MethodSource("anyAccess_parameters")
     public void getData_private_succeeds(String name, DatabaseAccessDto access) throws DatabaseUnavailableException,
             TableNotFoundException, QueryMalformedException, RemoteUnavailableException, PaginationException,
-            MetadataServiceException, NotAllowedException, DatabaseNotFoundException {
+            MetadataServiceException, NotAllowedException, DatabaseNotFoundException, StorageUnavailableException, FormatNotAvailableException {
         final Dataset<Row> mock = sparkSession.emptyDataFrame();
 
         /* mock */
@@ -451,7 +450,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn("GET");
 
         /* test */
-        final ResponseEntity<List<Map<String, Object>>> response = tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, httpServletRequest, USER_2_PRINCIPAL);
+        final ResponseEntity<?> response = tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, "application/json", httpServletRequest, USER_2_PRINCIPAL);
         assertEquals(HttpStatus.OK, response.getStatusCode());
     }
 
@@ -467,7 +466,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(TableNotFoundException.class, () -> {
-            tableEndpoint.getData(DATABASE_3_ID, TABLE_8_ID, null, null, null, httpServletRequest, null);
+            tableEndpoint.getData(DATABASE_3_ID, TABLE_8_ID, null, null, null, "application/json", httpServletRequest, null);
         });
     }
 
@@ -1323,8 +1322,9 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithAnonymousUser
-    public void exportData_publicDataPrivateSchema_succeeds() throws TableNotFoundException, NotAllowedException, StorageUnavailableException,
-            QueryMalformedException, RemoteUnavailableException, MetadataServiceException, DatabaseNotFoundException {
+    public void getData_publicDataPrivateSchemaTextCsv_succeeds() throws TableNotFoundException, NotAllowedException,
+            StorageUnavailableException, QueryMalformedException, RemoteUnavailableException, MetadataServiceException,
+            DatabaseNotFoundException, DatabaseUnavailableException, FormatNotAvailableException, PaginationException {
         final Dataset<Row> mock = sparkSession.emptyDataFrame();
 
         /* mock */
@@ -1334,18 +1334,21 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(DATABASE_1_PRIVILEGED_DTO);
         when(subsetService.getData(any(DatabaseDto.class), anyString()))
                 .thenReturn(mock);
+        when(httpServletRequest.getMethod())
+                .thenReturn("GET");
 
         /* test */
-        final ResponseEntity<InputStreamResource> response = tableEndpoint.exportDataset(DATABASE_1_ID, TABLE_4_ID, null, null);
+        final ResponseEntity<?> response = tableEndpoint.getData(DATABASE_1_ID, TABLE_4_ID, null, null, null, "text/csv", httpServletRequest, null);
         assertEquals(HttpStatus.OK, response.getStatusCode());
     }
 
     @ParameterizedTest
     @WithMockUser(username = USER_2_USERNAME)
     @MethodSource("anyAccess_parameters")
-    public void exportData_privateDataPrivateSchema_succeeds(String name, DatabaseAccessDto access)
+    public void getData_privateDataPrivateSchemaTextCsv_succeeds(String name, DatabaseAccessDto access)
             throws TableNotFoundException, NotAllowedException, StorageUnavailableException, QueryMalformedException,
-            RemoteUnavailableException, MetadataServiceException, DatabaseNotFoundException {
+            RemoteUnavailableException, MetadataServiceException, DatabaseNotFoundException,
+            DatabaseUnavailableException, FormatNotAvailableException, PaginationException {
         final Dataset<Row> mock = sparkSession.emptyDataFrame();
 
         /* mock */
@@ -1359,9 +1362,11 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(DATABASE_1_DTO);
         when(subsetService.getData(any(DatabaseDto.class), anyString()))
                 .thenReturn(mock);
+        when(httpServletRequest.getMethod())
+                .thenReturn("GET");
 
         /* test */
-        final ResponseEntity<InputStreamResource> response = tableEndpoint.exportDataset(DATABASE_1_ID, TABLE_1_ID, null, USER_2_PRINCIPAL);
+        final ResponseEntity<?> response = tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, "text/csv", httpServletRequest, USER_2_PRINCIPAL);
         assertEquals(HttpStatus.OK, response.getStatusCode());
     }
 
@@ -1381,7 +1386,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            tableEndpoint.exportDataset(DATABASE_1_ID, TABLE_1_ID, null, null);
+            tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, "text/csv", httpServletRequest, null);
         });
     }
 
diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java
index 0393be3dad8da280f29cc1be9133ffa42c9f6cb2..23ad1b73e3db99c355d08aa8011ab658fb48e728 100644
--- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java
+++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java
@@ -29,7 +29,6 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
 import java.sql.SQLException;
 import java.time.Instant;
 import java.util.List;
-import java.util.Map;
 
 import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -253,7 +252,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(DATABASE_1_PRIVILEGED_DTO);
         doNothing()
                 .when(viewService)
-                .delete(DATABASE_1_PRIVILEGED_DTO,VIEW_1_DTO);
+                .delete(DATABASE_1_PRIVILEGED_DTO, VIEW_1_DTO);
 
         /* test */
         assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
@@ -281,7 +280,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
     @WithMockUser(username = USER_1_USERNAME, authorities = {"view-database-view-data"})
     public void getData_privateDataPrivateSchema_succeeds() throws RemoteUnavailableException, ViewNotFoundException,
             DatabaseUnavailableException, QueryMalformedException, PaginationException, NotAllowedException,
-            MetadataServiceException, TableNotFoundException, DatabaseNotFoundException {
+            MetadataServiceException, TableNotFoundException, DatabaseNotFoundException, ViewMalformedException, StorageUnavailableException, FormatNotAvailableException {
         final Dataset<Row> mock = sparkSession.emptyDataFrame();
 
         /* mock */
@@ -297,7 +296,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn("GET");
 
         /* test */
-        final ResponseEntity<List<Map<String, Object>>> response = viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, null, null, null, httpServletRequest, USER_1_PRINCIPAL);
+        final ResponseEntity<?> response = viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, null, null, null, httpServletRequest, "application/json", USER_1_PRINCIPAL);
         assertEquals(HttpStatus.OK, response.getStatusCode());
         assertNotNull(response.getBody());
     }
@@ -323,7 +322,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, null, null, null, httpServletRequest, null);
+            viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, null, null, null, httpServletRequest, "application/json", null);
         });
     }
 
@@ -331,7 +330,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
     @WithMockUser(username = USER_1_USERNAME, authorities = {"view-database-view-data"})
     public void getData_privateHead_succeeds() throws RemoteUnavailableException, ViewNotFoundException,
             SQLException, DatabaseUnavailableException, QueryMalformedException, PaginationException,
-            NotAllowedException, MetadataServiceException, TableNotFoundException, DatabaseNotFoundException {
+            NotAllowedException, MetadataServiceException, TableNotFoundException, DatabaseNotFoundException, ViewMalformedException, StorageUnavailableException, FormatNotAvailableException {
 
         /* mock */
         when(credentialService.getView(DATABASE_1_ID, VIEW_3_ID))
@@ -346,7 +345,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(VIEW_3_DATA_COUNT);
 
         /* test */
-        final ResponseEntity<List<Map<String, Object>>> response = viewEndpoint.getData(DATABASE_1_ID, VIEW_3_ID, null, null, null, httpServletRequest, USER_1_PRINCIPAL);
+        final ResponseEntity<?> response = viewEndpoint.getData(DATABASE_1_ID, VIEW_3_ID, null, null, null, httpServletRequest, "application/json", USER_1_PRINCIPAL);
         assertEquals(HttpStatus.OK, response.getStatusCode());
         assertNotNull(response.getHeaders().get("X-Count"));
         assertEquals(1, response.getHeaders().get("X-Count").size());
@@ -373,13 +372,13 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, null, null, null, httpServletRequest, USER_4_PRINCIPAL);
+            viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, null, null, null, httpServletRequest, "application/json", USER_4_PRINCIPAL);
         });
     }
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"view-database-view-data"})
-    public void getData_viewNotFound_fails() throws RemoteUnavailableException, ViewNotFoundException,
+    public void getData_viewNotFoundTextCsv_fails() throws RemoteUnavailableException, ViewNotFoundException,
             MetadataServiceException {
 
         /* mock */
@@ -389,7 +388,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(ViewNotFoundException.class, () -> {
-            viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, null, null, null, httpServletRequest, USER_4_PRINCIPAL);
+            viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, null, null, null, httpServletRequest, "text/csv", USER_4_PRINCIPAL);
         });
     }
 
@@ -407,13 +406,13 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, null, null, null, httpServletRequest, USER_4_PRINCIPAL);
+            viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, null, null, null, httpServletRequest, "application/json", USER_4_PRINCIPAL);
         });
     }
 
     @Test
     @WithMockUser(username = USER_3_USERNAME, authorities = {"view-database-view-data"})
-    public void exportDataset_privateNoAccess_fails() throws RemoteUnavailableException, ViewNotFoundException,
+    public void getData_privateNoAccessTextCsv_fails() throws RemoteUnavailableException, ViewNotFoundException,
             NotAllowedException, MetadataServiceException {
 
         /* mock */
@@ -425,13 +424,13 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            viewEndpoint.exportDataset(DATABASE_1_ID, VIEW_1_ID, null, USER_4_PRINCIPAL);
+            viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, null, null, null, httpServletRequest, "text/csv", USER_4_PRINCIPAL);
         });
     }
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"view-database-view-data"})
-    public void exportDataset_viewNotFound_fails() throws RemoteUnavailableException, ViewNotFoundException,
+    public void getData_viewNotFound_fails() throws RemoteUnavailableException, ViewNotFoundException,
             MetadataServiceException {
 
         /* mock */
@@ -441,25 +440,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(ViewNotFoundException.class, () -> {
-            viewEndpoint.exportDataset(DATABASE_1_ID, VIEW_1_ID, null, USER_4_PRINCIPAL);
-        });
-    }
-
-    @Test
-    @WithMockUser(username = USER_1_USERNAME, authorities = {"view-database-view-data"})
-    public void exportDataset_privateNoAccess_succeeds() throws RemoteUnavailableException, ViewNotFoundException,
-            NotAllowedException, MetadataServiceException {
-
-        /* mock */
-        when(credentialService.getView(DATABASE_1_ID, VIEW_1_ID))
-                .thenReturn(VIEW_1_DTO);
-        doThrow(NotAllowedException.class)
-                .when(credentialService)
-                .getAccess(DATABASE_1_ID, USER_4_ID);
-
-        /* test */
-        assertThrows(NotAllowedException.class, () -> {
-            viewEndpoint.exportDataset(DATABASE_1_ID, VIEW_1_ID, null, USER_4_PRINCIPAL);
+            viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, null, null, null, httpServletRequest, "application/json", USER_4_PRINCIPAL);
         });
     }
 
diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java
index ae03fdbf68cefc1db05455d0ec228ea00be1c12f..33f77b7cce1f3fc29f70073bc9bbc6e5acd5869e 100644
--- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java
+++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java
@@ -1,8 +1,6 @@
 package at.tuwien.mvc;
 
-import at.tuwien.api.database.query.ExecuteStatementDto;
 import at.tuwien.api.database.query.ImportDto;
-import at.tuwien.api.database.query.QueryDto;
 import at.tuwien.api.database.query.QueryPersistDto;
 import at.tuwien.api.database.table.TupleDeleteDto;
 import at.tuwien.api.database.table.TupleDto;
@@ -116,7 +114,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest {
             /* ignore */
         }
         try {
-            subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, 0L, 10L);
+            subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, "application/json", httpServletRequest, null, 0L, 10L);
         } catch (Exception e) {
             /* ignore */
         }
@@ -126,7 +124,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest {
             /* ignore */
         }
         try {
-            subsetEndpoint.findById(DATABASE_1_ID, QUERY_1_ID, "application/json", null, USER_1_PRINCIPAL);
+            subsetEndpoint.findById(DATABASE_1_ID, QUERY_1_ID, null, USER_1_PRINCIPAL);
         } catch (Exception e) {
             /* ignore */
         }
@@ -145,7 +143,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest {
 
         /* mock */
         try {
-            tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, httpServletRequest, null);
+            tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, "application/json", httpServletRequest, null);
         } catch (Exception e) {
             /* ignore */
         }
@@ -169,11 +167,6 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest {
         } catch (Exception e) {
             /* ignore */
         }
-        try {
-            tableEndpoint.exportDataset(DATABASE_1_ID, TABLE_1_ID, null, USER_1_PRINCIPAL);
-        } catch (Exception e) {
-            /* ignore */
-        }
         try {
             tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, ImportDto.builder().build(), USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         } catch (Exception e) {
@@ -195,7 +188,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest {
 
         /* mock */
         try {
-            viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, 0L, 10L, null, httpServletRequest, USER_1_PRINCIPAL);
+            viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, 0L, 10L, null, httpServletRequest, "application/json", USER_1_PRINCIPAL);
         } catch (Exception e) {
             /* ignore */
         }
diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MetadataMapper.java b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MetadataMapper.java
index 7bdf8fa14118f7f6cca621cf01e7567ce608dfdc..0bd399d454d4427440684a57d3f2611079279bca 100644
--- a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MetadataMapper.java
+++ b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MetadataMapper.java
@@ -6,7 +6,6 @@ import at.tuwien.api.database.DatabaseBriefDto;
 import at.tuwien.api.database.DatabaseDto;
 import at.tuwien.api.database.ViewColumnDto;
 import at.tuwien.api.database.ViewDto;
-import at.tuwien.api.database.query.QueryDto;
 import at.tuwien.api.database.table.TableBriefDto;
 import at.tuwien.api.database.table.TableDto;
 import at.tuwien.api.database.table.columns.ColumnDto;
@@ -27,10 +26,6 @@ public interface MetadataMapper {
 
     org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MetadataMapper.class);
 
-    default String queryDtoToViewName(QueryDto subset) {
-        return subset.getQueryHash();
-    }
-
     ContainerDto containerDtoToContainerDto(ContainerDto data);
 
     DatabaseBriefDto databaseDtoToDatabaseBriefDto(DatabaseDto data);
diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/CredentialService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/CredentialService.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8448528bda815a6f0fa0faba462b2a37fae1b95
--- /dev/null
+++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/CredentialService.java
@@ -0,0 +1,7 @@
+package at.tuwien.service;
+
+import at.tuwien.api.keycloak.TokenDto;
+
+public interface CredentialService {
+    TokenDto getAccessToken(String username, String password);
+}
diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/CredentialServiceImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/CredentialServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..7cf7d1eff4394ae174b0e724fa8e34455b363578
--- /dev/null
+++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/CredentialServiceImpl.java
@@ -0,0 +1,44 @@
+package at.tuwien.service.impl;
+
+import at.tuwien.api.keycloak.TokenDto;
+import at.tuwien.gateway.KeycloakGateway;
+import at.tuwien.service.CredentialService;
+import com.github.benmanes.caffeine.cache.Cache;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Log4j2
+@Service
+public class CredentialServiceImpl implements CredentialService {
+
+    private final KeycloakGateway keycloakGateway;
+    private final Cache<String, TokenDto> tokenCache;
+
+    @Autowired
+    public CredentialServiceImpl(KeycloakGateway keycloakGateway, Cache<String, TokenDto> tokenCache) {
+        this.tokenCache = tokenCache;
+        this.keycloakGateway = keycloakGateway;
+    }
+
+    @Override
+    public TokenDto getAccessToken(String username, String password) {
+        final TokenDto cacheAccessToken = tokenCache.getIfPresent(username);
+        if (cacheAccessToken != null) {
+            log.trace("found access token for user with username {} in cache", username);
+            return cacheAccessToken;
+        }
+        log.debug("access token for user with username {} not it cache (anymore): request new", username);
+        final TokenDto token = keycloakGateway.obtainUserToken(username, password);
+        tokenCache.put(username, token);
+        return token;
+    }
+
+    /**
+     * Method for test cases to remove all caches.
+     */
+    public void invalidateAll() {
+        tokenCache.invalidateAll();
+    }
+
+}
diff --git a/dbrepo-gateway-service/dbrepo.conf b/dbrepo-gateway-service/dbrepo.conf
index 165aba7643fcda32b94784d757d93e253814a1e0..f181fdf7a70945280453f345f6a639921967abca 100644
--- a/dbrepo-gateway-service/dbrepo.conf
+++ b/dbrepo-gateway-service/dbrepo.conf
@@ -123,7 +123,7 @@ server {
         proxy_read_timeout      90;
     }
 
-    location ~ "/api/database/([0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/table/([0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/(data|history|export)" {
+    location ~ "/api/database/([0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/table/([0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/(data|history)" {
         proxy_set_header        Host $host;
         proxy_set_header        X-Real-IP $remote_addr;
         proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
@@ -132,7 +132,7 @@ server {
         proxy_read_timeout      90;
     }
 
-    location ~ "/api/database/([0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/view/([0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/(data|export)" {
+    location ~ "/api/database/([0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/view/([0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/data" {
         proxy_set_header        Host $host;
         proxy_set_header        X-Real-IP $remote_addr;
         proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
@@ -168,6 +168,15 @@ server {
         proxy_read_timeout      90;
     }
 
+    location ~ "/api/identifier/([0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})" {
+        proxy_set_header        Host $host;
+        proxy_set_header        X-Real-IP $remote_addr;
+        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header        X-Forwarded-Proto $scheme;
+        proxy_pass              http://metadata;
+        proxy_read_timeout      90;
+    }
+
     location ~ "/pid/([0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})" {
         rewrite /pid/(.*) /api/identifier/$1 break;
         proxy_set_header        Host $host;
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java
index 57ecb5ad625017fec1571a1da53a83cbd5fcf852..0bc16d61cd9a1dc45c129184ec18cfb49ed0d754 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java
@@ -46,6 +46,9 @@ public class IdentifierDto {
     @Schema(example = "null")
     private UUID viewId;
 
+    @NotNull
+    private LinksDto links;
+
     @NotNull
     @Schema(example = "database")
     private IdentifierTypeDto type;
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/LinksDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/LinksDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..fcef2a659750d9cf6b78bdc54b4a627dbcc0935d
--- /dev/null
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/LinksDto.java
@@ -0,0 +1,32 @@
+package at.tuwien.api.identifier;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.*;
+import lombok.extern.jackson.Jacksonized;
+
+
+@Getter
+@Setter
+@Builder
+@EqualsAndHashCode
+@NoArgsConstructor
+@AllArgsConstructor
+@Jacksonized
+@ToString
+public class LinksDto {
+
+    @NotNull
+    @Schema(example = "http://example.com/api/")
+    private String self;
+
+    @NotNull
+    @JsonProperty("self_html")
+    @Schema(example = "http://example.com")
+    private String selfHtml;
+
+    @Schema(example = "http://example.com")
+    private String data;
+
+}
diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java
index 2d18ca0c49124539a6b9aa6072396aa247eb8167..74284789247ee20deac7df99c49a4f60e8cfa765 100644
--- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java
+++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java
@@ -318,10 +318,27 @@ public interface MetadataMapper {
     Identifier identifierDtoToIdentifier(IdentifierDto data);
 
     @Mappings({
-            @Mapping(target = "databaseId", source = "database.id")
+            @Mapping(target = "databaseId", source = "database.id"),
+            @Mapping(target = "links", expression = "java(identifierToLinksDto(data))"),
     })
     IdentifierDto identifierToIdentifierDto(Identifier data);
 
+    default LinksDto identifierToLinksDto(Identifier data) {
+        final LinksDto links = LinksDto.builder()
+                .self("/api/identifier/" + data.getId())
+                .selfHtml("/pid/" + data.getId())
+                .build();
+        switch (data.getType()) {
+            case VIEW ->
+                    links.setData("/api/database/" + data.getDatabase().getId() + "/view/" + data.getViewId() + "/data");
+            case TABLE ->
+                    links.setData("/api/database/" + data.getDatabase().getId() + "/table/" + data.getTableId() + "/data");
+            case SUBSET ->
+                    links.setData("/api/database/" + data.getDatabase().getId() + "/subset/" + data.getQueryId() + "/data");
+        }
+        return links;
+    }
+
     @Mappings({
             @Mapping(target = "databaseId", source = "database.id"),
             @Mapping(target = "ownedBy", source = "owner.id")
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java
index 31dd1e542d53112ff42b49a6e5f6269255088ac6..96deaea775defd1562aba3601d9ba513b771f401 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java
@@ -9,7 +9,6 @@ import at.tuwien.entities.database.Database;
 import at.tuwien.entities.database.DatabaseAccess;
 import at.tuwien.entities.identifier.Identifier;
 import at.tuwien.entities.identifier.IdentifierStatusType;
-import at.tuwien.entities.identifier.IdentifierType;
 import at.tuwien.entities.user.User;
 import at.tuwien.exception.*;
 import at.tuwien.mapper.MetadataMapper;
@@ -126,7 +125,7 @@ public class IdentifierEndpoint extends AbstractEndpoint {
     }
 
     @GetMapping(value = "/{identifierId}", produces = {MediaType.APPLICATION_JSON_VALUE, "application/ld+json",
-            MediaType.TEXT_XML_VALUE, "text/csv", "text/bibliography", "text/bibliography; style=apa",
+            MediaType.TEXT_XML_VALUE, "text/bibliography", "text/bibliography; style=apa",
             "text/bibliography; style=ieee", "text/bibliography; style=bibtex"})
     @Transactional(readOnly = true)
     @Observed(name = "dbrepo_identifier_find")
@@ -138,7 +137,6 @@ public class IdentifierEndpoint extends AbstractEndpoint {
                     content = {
                             @Content(mediaType = "application/json", schema = @Schema(implementation = IdentifierDto.class)),
                             @Content(mediaType = "application/ld+json", schema = @Schema(implementation = LdDatasetDto.class)),
-                            @Content(mediaType = "text/csv"),
                             @Content(mediaType = "text/xml"),
                             @Content(mediaType = "text/bibliography"),
                             @Content(mediaType = "text/bibliography; style=apa"),
@@ -190,7 +188,7 @@ public class IdentifierEndpoint extends AbstractEndpoint {
                                   @RequestHeader(HttpHeaders.ACCEPT) String accept,
                                   Principal principal) throws IdentifierNotFoundException,
             DataServiceException, DataServiceConnectionException, MalformedException, FormatNotAvailableException,
-            QueryNotFoundException, NotAllowedException {
+            QueryNotFoundException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         log.debug("endpoint find identifier, identifierId={}, accept={}", identifierId, accept);
         if (accept == null) {
             accept = "";
@@ -212,13 +210,6 @@ public class IdentifierEndpoint extends AbstractEndpoint {
             case "application/ld+json":
                 log.trace("accept header matches json-ld");
                 return ResponseEntity.ok(metadataMapper.identifierToLdDatasetDto(identifier, endpointConfig.getWebsiteUrl()));
-            case "text/csv":
-                log.trace("accept header matches csv");
-                if (identifier.getType().equals(IdentifierType.DATABASE)) {
-                    log.error("Failed to export dataset: identifier type is database");
-                    throw new FormatNotAvailableException("Failed to export dataset: identifier type is database");
-                }
-                return ResponseEntity.ok(identifierService.exportResource(identifier));
             case "text/xml":
                 log.trace("accept header matches xml");
                 return ResponseEntity.ok(identifierService.exportMetadata(identifier));
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java
index 1e003041027ed22072d167eae81fab3a5d32c813..4f6bd37fd41dabb1bdb73eadc15fddf06aa2f93c 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java
@@ -410,7 +410,8 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @Test
     @WithMockUser(username = USER_2_USERNAME)
     public void find_draft_succeeds() throws IdentifierNotFoundException, MalformedException, NotAllowedException,
-            DataServiceException, QueryNotFoundException, DataServiceConnectionException, FormatNotAvailableException {
+            DataServiceException, QueryNotFoundException, DataServiceConnectionException, FormatNotAvailableException,
+            TableNotFoundException, ViewNotFoundException {
 
         /* mock */
         when(identifierService.find(IDENTIFIER_5_ID))
@@ -424,7 +425,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithAnonymousUser
     public void find_defaultHtmlRespondsJson_succeeds() throws IdentifierNotFoundException, MalformedException,
             NotAllowedException, DataServiceException, QueryNotFoundException, DataServiceConnectionException,
-            FormatNotAvailableException {
+            FormatNotAvailableException, TableNotFoundException, ViewNotFoundException {
 
         /* mock */
         when(identifierService.find(IDENTIFIER_1_ID))
@@ -438,7 +439,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithMockUser(username = USER_4_USERNAME)
     public void find_json0_succeeds() throws IOException, MalformedException, DataServiceException,
             DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "application/json";
         final IdentifierDto compare = objectMapper.readValue(FileUtils.readFileToString(new File("src/test/resources/json/metadata0.json"), StandardCharsets.UTF_8), IdentifierDto.class);
 
@@ -468,7 +469,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithAnonymousUser
     public void find_json1_succeeds() throws IOException, MalformedException, DataServiceException,
             DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "application/json";
         final IdentifierDto compare = objectMapper.readValue(FileUtils.readFileToString(new File("src/test/resources/json/metadata1.json"), StandardCharsets.UTF_8), IdentifierDto.class);
 
@@ -514,34 +515,11 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
         assertEquals(compare.getCreators().get(0).getNameIdentifierScheme(), creator0.getNameIdentifierScheme());
     }
 
-    @Test
-    @WithAnonymousUser
-    public void find_csv_succeeds() throws IOException, MalformedException, DataServiceException,
-            DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
-        final String accept = "text/csv";
-        final InputStreamResource compare = new InputStreamResource(FileUtils.openInputStream(new File("src/test/resources/csv/keyboard.csv")));
-        final InputStreamResource mock = new InputStreamResource(FileUtils.openInputStream(new File("src/test/resources/csv/keyboard.csv")));
-
-        /* mock */
-        when(identifierService.find(IDENTIFIER_2_ID))
-                .thenReturn(IDENTIFIER_2);
-        when(identifierService.exportResource(IDENTIFIER_2))
-                .thenReturn(mock);
-
-        /* test */
-        final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_2_ID, accept, null);
-        assertEquals(HttpStatus.OK, response.getStatusCode());
-        final InputStreamResource body = (InputStreamResource) response.getBody();
-        assertNotNull(body);
-        assertEquals(inputStreamToString(compare.getInputStream()), inputStreamToString(body.getInputStream()));
-    }
-
     @Test
     @WithAnonymousUser
     public void find_bibliography_succeeds() throws IOException, MalformedException, DataServiceException,
             DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "text/bibliography";
         final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_apa1.txt"),
                 StandardCharsets.UTF_8);
@@ -584,7 +562,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithMockUser(username = USER_4_USERNAME)
     public void find_bibliographyApa0_succeeds() throws IOException, MalformedException, DataServiceException,
             DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "text/bibliography; style=apa";
         final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_apa0.txt"),
                 StandardCharsets.UTF_8);
@@ -607,7 +585,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithAnonymousUser
     public void find_bibliographyApa1_succeeds() throws IOException, MalformedException, DataServiceException,
             DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "text/bibliography; style=apa";
         final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_apa1.txt"),
                 StandardCharsets.UTF_8);
@@ -630,7 +608,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithMockUser(username = USER_2_USERNAME)
     public void find_draftBibliographyApa2_succeeds() throws IOException, MalformedException, DataServiceException,
             DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "text/bibliography; style=apa";
         final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_apa2.txt"),
                 StandardCharsets.UTF_8);
@@ -653,7 +631,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithAnonymousUser
     public void find_bibliographyApa3_succeeds() throws IOException, MalformedException, DataServiceException,
             DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "text/bibliography; style=apa";
         final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_apa3.txt"),
                 StandardCharsets.UTF_8);
@@ -676,7 +654,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithAnonymousUser
     public void find_bibliographyApa4_succeeds() throws IOException, MalformedException, DataServiceException,
             DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "text/bibliography; style=apa";
         final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_apa4.txt"),
                 StandardCharsets.UTF_8);
@@ -699,7 +677,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithMockUser(username = USER_4_USERNAME)
     public void find_bibliographyIeee0_succeeds() throws IOException, MalformedException, DataServiceException,
             DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "text/bibliography; style=ieee";
         final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_ieee0.txt"),
                 StandardCharsets.UTF_8);
@@ -722,7 +700,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithAnonymousUser
     public void find_bibliographyIeee1_succeeds() throws IOException, MalformedException, DataServiceException,
             DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "text/bibliography; style=ieee";
         final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_ieee1.txt"),
                 StandardCharsets.UTF_8);
@@ -745,7 +723,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithMockUser(username = USER_2_USERNAME)
     public void find_bibliographyIeee2_succeeds() throws IOException, MalformedException, DataServiceException,
             DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "text/bibliography; style=ieee";
         final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_ieee2.txt"),
                 StandardCharsets.UTF_8);
@@ -768,7 +746,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithAnonymousUser
     public void find_bibliographyIeee3_succeeds() throws IOException, MalformedException, DataServiceException,
             DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "text/bibliography; style=ieee";
         final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_ieee3.txt"),
                 StandardCharsets.UTF_8);
@@ -791,7 +769,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithMockUser(username = USER_4_USERNAME)
     public void find_bibliographyBibtex0_succeeds() throws IOException, MalformedException, DataServiceException,
             DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "text/bibliography; style=bibtex";
         final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_bibtex0.txt"),
                 StandardCharsets.UTF_8);
@@ -814,7 +792,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithAnonymousUser
     public void find_bibliographyBibtex1_succeeds() throws MalformedException, IOException, DataServiceException,
             DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "text/bibliography; style=bibtex";
         final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_bibtex1.txt"),
                 StandardCharsets.UTF_8);
@@ -837,7 +815,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithMockUser(username = USER_2_USERNAME)
     public void find_bibliographyBibtex2_succeeds() throws MalformedException, DataServiceException, IOException,
             DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "text/bibliography; style=bibtex";
         final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_bibtex2.txt"),
                 StandardCharsets.UTF_8);
@@ -860,7 +838,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithAnonymousUser
     public void find_bibliographyBibtex3_succeeds() throws MalformedException, DataServiceException,
             DataServiceConnectionException, IOException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException, NotAllowedException {
+            FormatNotAvailableException, NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final String accept = "text/bibliography; style=bibtex";
         final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_bibtex3.txt"),
                 StandardCharsets.UTF_8);
@@ -882,7 +860,8 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @Test
     @WithAnonymousUser
     public void find_jsonLd_succeeds() throws MalformedException, DataServiceException, DataServiceConnectionException,
-            QueryNotFoundException, IdentifierNotFoundException, FormatNotAvailableException, NotAllowedException {
+            QueryNotFoundException, IdentifierNotFoundException, FormatNotAvailableException, NotAllowedException,
+            TableNotFoundException, ViewNotFoundException {
         final String accept = "application/ld+json";
 
         /* mock */
@@ -914,7 +893,8 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @Test
     @WithAnonymousUser
     public void find_move_succeeds() throws MalformedException, DataServiceException, DataServiceConnectionException,
-            QueryNotFoundException, IdentifierNotFoundException, FormatNotAvailableException, NotAllowedException {
+            QueryNotFoundException, IdentifierNotFoundException, FormatNotAvailableException, NotAllowedException,
+            TableNotFoundException, ViewNotFoundException {
 
         /* mock */
         when(identifierService.find(IDENTIFIER_1_ID))
@@ -1065,7 +1045,8 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @Test
     @WithAnonymousUser
     public void find_json_succeeds() throws MalformedException, DataServiceException, DataServiceConnectionException,
-            FormatNotAvailableException, QueryNotFoundException, IdentifierNotFoundException, NotAllowedException {
+            FormatNotAvailableException, QueryNotFoundException, IdentifierNotFoundException, NotAllowedException,
+            TableNotFoundException, ViewNotFoundException {
         final String accept = "application/json";
 
         /* mock */
@@ -1093,7 +1074,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @WithAnonymousUser
     public void find_xml_succeeds() throws MalformedException, DataServiceException, DataServiceConnectionException,
             IOException, QueryNotFoundException, IdentifierNotFoundException, FormatNotAvailableException,
-            NotAllowedException {
+            NotAllowedException, TableNotFoundException, ViewNotFoundException {
         final InputStreamResource resource = new InputStreamResource(FileUtils.openInputStream(
                 new File("src/test/resources/xml/datacite-example-dataset-v4.xml")));
 
@@ -1108,9 +1089,9 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithAnonymousUser
-    public void find_httpRedirect_succeeds() throws MalformedException, DataServiceException,
-            DataServiceConnectionException, FormatNotAvailableException, QueryNotFoundException,
-            IdentifierNotFoundException, NotAllowedException {
+    public void find_httpRedirect_succeeds() throws MalformedException, DataServiceException, QueryNotFoundException,
+            DataServiceConnectionException, FormatNotAvailableException, IdentifierNotFoundException,
+            NotAllowedException, TableNotFoundException, ViewNotFoundException {
 
         /* test */
         final ResponseEntity<?> response = generic_find(null, null);
@@ -1509,14 +1490,13 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
 
     protected ResponseEntity<?> generic_find(String accept, InputStreamResource resource)
             throws MalformedException, DataServiceException, DataServiceConnectionException, FormatNotAvailableException,
-            QueryNotFoundException, IdentifierNotFoundException, NotAllowedException {
+            QueryNotFoundException, IdentifierNotFoundException, NotAllowedException, TableNotFoundException,
+            ViewNotFoundException {
 
         /* mock */
         when(identifierService.find(IDENTIFIER_1_ID))
                 .thenReturn(IDENTIFIER_1);
         if (resource != null) {
-            when(identifierService.exportResource(IDENTIFIER_1))
-                    .thenReturn(resource);
             when(identifierService.exportMetadata(IDENTIFIER_1))
                     .thenReturn(resource);
         }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java
index 5508a6245e40021cf307d474bc056dfc64861920..af1ecc9f1c69b6f2acaf3176575045bf14ad8322 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java
@@ -1,6 +1,5 @@
 package at.tuwien.gateway;
 
-import at.tuwien.ExportResourceDto;
 import at.tuwien.api.database.AccessTypeDto;
 import at.tuwien.api.database.DatabaseDto;
 import at.tuwien.api.database.ViewDto;
@@ -812,75 +811,6 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest {
         });
     }
 
-    @Test
-    public void exportQuery_succeeds() throws DataServiceException, DataServiceConnectionException,
-            QueryNotFoundException {
-
-        /* mock */
-        when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ExportResourceDto.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.OK)
-                        .build());
-
-        /* test */
-        dataServiceGateway.exportQuery(DATABASE_1_ID, QUERY_1_ID);
-    }
-
-    @Test
-    public void exportQuery_connection_fails() {
-
-        /* mock */
-        doThrow(HttpServerErrorException.class)
-                .when(dataServiceRestTemplate)
-                .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ExportResourceDto.class));
-
-        /* test */
-        assertThrows(DataServiceConnectionException.class, () -> {
-            dataServiceGateway.exportQuery(DATABASE_1_ID, QUERY_1_ID);
-        });
-    }
-
-    @Test
-    public void exportQuery_unauthorized_fails() {
-
-        /* mock */
-        doThrow(HttpClientErrorException.Unauthorized.class)
-                .when(dataServiceRestTemplate)
-                .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ExportResourceDto.class));
-
-        /* test */
-        assertThrows(DataServiceException.class, () -> {
-            dataServiceGateway.exportQuery(DATABASE_1_ID, QUERY_1_ID);
-        });
-    }
-
-    @Test
-    public void exportQuery_notFound_fails() {
-
-        /* mock */
-        doThrow(HttpClientErrorException.NotFound.class)
-                .when(dataServiceRestTemplate)
-                .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ExportResourceDto.class));
-
-        /* test */
-        assertThrows(QueryNotFoundException.class, () -> {
-            dataServiceGateway.exportQuery(DATABASE_1_ID, QUERY_1_ID);
-        });
-    }
-
-    @Test
-    public void exportQuery_responseCode_fails() {
-
-        /* mock */
-        when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ExportResourceDto.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT)
-                        .build());
-
-        /* test */
-        assertThrows(DataServiceException.class, () -> {
-            dataServiceGateway.exportQuery(DATABASE_1_ID, QUERY_1_ID);
-        });
-    }
-
     @Test
     public void getTableSchemas_succeeds() throws DataServiceException, DataServiceConnectionException, TableNotFoundException {
 
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/InternalRequestInterceptor.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/InternalRequestInterceptor.java
index d6b5da373cd08436c1ab1a42031952f05faf6d12..6d7480ba8034a9108492ab0816005f4a92f8046c 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/InternalRequestInterceptor.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/InternalRequestInterceptor.java
@@ -29,7 +29,9 @@ public class InternalRequestInterceptor implements ClientHttpRequestInterceptor
     public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
             throws IOException {
         final HttpHeaders headers = request.getHeaders();
-        headers.setAccept(List.of(MediaType.APPLICATION_JSON));
+        if (headers.get("Accept") == null) {
+            headers.setAccept(List.of(MediaType.APPLICATION_JSON));
+        }
         final TokenDto token = credentialService.getAccessToken(gatewayConfig.getSystemUsername(),
                 gatewayConfig.getSystemPassword());
         headers.setBearerAuth(token.getAccessToken());
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/DataServiceGateway.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/DataServiceGateway.java
index 5bb0303e16c14145f1e08ee913b4c4dcabdead61..3996448d212bed80364bdc2ac4d5ab62da3e1863 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/DataServiceGateway.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/DataServiceGateway.java
@@ -1,9 +1,8 @@
 package at.tuwien.gateway;
 
-import at.tuwien.ExportResourceDto;
 import at.tuwien.api.database.AccessTypeDto;
-import at.tuwien.api.database.DatabaseDto;
 import at.tuwien.api.database.CreateViewDto;
+import at.tuwien.api.database.DatabaseDto;
 import at.tuwien.api.database.ViewDto;
 import at.tuwien.api.database.internal.CreateDatabaseDto;
 import at.tuwien.api.database.query.QueryDto;
@@ -145,19 +144,6 @@ public interface DataServiceGateway {
     QueryDto findQuery(UUID databaseId, UUID queryId) throws DataServiceConnectionException, DataServiceException,
             QueryNotFoundException;
 
-    /**
-     * Exports a given query.
-     *
-     * @param databaseId The database id.
-     * @param queryId    The query id.
-     * @return The exported resource, if successful.
-     * @throws DataServiceConnectionException The connection to the data service could not be established.
-     * @throws DataServiceException           The data service responded unexpectedly.
-     * @throws QueryNotFoundException         The given query was not found in the query store.
-     */
-    ExportResourceDto exportQuery(UUID databaseId, UUID queryId) throws DataServiceConnectionException,
-            DataServiceException, QueryNotFoundException;
-
     /**
      * Obtain table schemas from a given database.
      *
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java
index 8feed3b291444ea674317673d74fa59bba4b5cad..ba855feda32000f1c4f0265c2b194c7e9e4e8237 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java
@@ -1,6 +1,5 @@
 package at.tuwien.gateway.impl;
 
-import at.tuwien.ExportResourceDto;
 import at.tuwien.api.database.*;
 import at.tuwien.api.database.internal.CreateDatabaseDto;
 import at.tuwien.api.database.query.QueryDto;
@@ -315,31 +314,6 @@ public class DataServiceGatewayImpl implements DataServiceGateway {
         return response.getBody();
     }
 
-    @Override
-    public ExportResourceDto exportQuery(UUID databaseId, UUID queryId) throws DataServiceConnectionException,
-            DataServiceException, QueryNotFoundException {
-        final ResponseEntity<ExportResourceDto> response;
-        final String path = "/api/database/" + databaseId + "/subset/" + queryId;
-        log.trace("export subset at endpoint {} with path {}", gatewayConfig.getDataEndpoint(), path);
-        try {
-            response = restTemplate.exchange(path, HttpMethod.GET, HttpEntity.EMPTY, ExportResourceDto.class);
-        } catch (HttpServerErrorException e) {
-            log.error("Failed to export query: {}", e.getMessage());
-            throw new DataServiceConnectionException("Failed to export query: " + e.getMessage(), e);
-        } catch (HttpClientErrorException.NotFound e) {
-            log.error("Failed to export query: not found: {}", e.getMessage());
-            throw new QueryNotFoundException("Failed to export query: not found: " + e.getMessage(), e);
-        } catch (HttpClientErrorException.Unauthorized e) {
-            log.error("Failed to export query: {}", e.getMessage());
-            throw new DataServiceException("Failed to export query: " + e.getMessage(), e);
-        }
-        if (!response.getStatusCode().equals(HttpStatus.OK)) {
-            log.error("Failed to export query: wrong http code: {}", response.getStatusCode());
-            throw new DataServiceException("Failed to export query: wrong http code: " + response.getStatusCode());
-        }
-        return response.getBody();
-    }
-
     @Override
     public List<TableDto> getTableSchemas(UUID databaseId) throws DataServiceConnectionException, DataServiceException,
             TableNotFoundException {
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/IdentifierService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/IdentifierService.java
index 605ded7d37faad52fc7119f4dd0a958c199b68b8..8c9a3088fe96844df0628ba95d71c0356ebc2b33 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/IdentifierService.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/IdentifierService.java
@@ -161,19 +161,6 @@ public interface IdentifierService {
      */
     String exportBibliography(Identifier identifier, BibliographyTypeDto style) throws MalformedException;
 
-    /**
-     * Exports an identifier to XML
-     *
-     * @param identifier The identifier.
-     * @return The XML resource, if successful.
-     * @throws DataServiceException
-     * @throws DataServiceConnectionException
-     * @throws IdentifierNotFoundException
-     * @throws QueryNotFoundException
-     */
-    InputStreamResource exportResource(Identifier identifier) throws DataServiceException, DataServiceConnectionException,
-            IdentifierNotFoundException, QueryNotFoundException;
-
     /**
      * Soft-deletes an identifier for a given id in the metadata database. Does not actually remove the entity from the
      * database, but sets it as deleted.
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java
index 0c8ce27d1214b1ac47c6756d7f5d3fbe4c77adcd..5ab9cc26a226374d817966e880a684393c36cf08 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java
@@ -196,13 +196,6 @@ public class DataCiteIdentifierServiceImpl implements IdentifierService {
         return identifierService.exportBibliography(identifier, style);
     }
 
-    @Override
-    @Transactional(readOnly = true)
-    public InputStreamResource exportResource(Identifier identifier) throws DataServiceException,
-            DataServiceConnectionException, IdentifierNotFoundException, QueryNotFoundException {
-        return identifierService.exportResource(identifier);
-    }
-
     @Override
     @Transactional
     public void delete(Identifier identifier) throws DataServiceException, DataServiceConnectionException,
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java
index b1d1f45932c61bb755a615f22a6a919297ffec46..8af7023c8cb48b64078457c409b8e14ece6187b7 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java
@@ -1,6 +1,5 @@
 package at.tuwien.service.impl;
 
-import at.tuwien.ExportResourceDto;
 import at.tuwien.api.database.query.QueryDto;
 import at.tuwien.api.identifier.BibliographyTypeDto;
 import at.tuwien.api.identifier.CreateIdentifierDto;
@@ -356,14 +355,6 @@ public class IdentifierServiceImpl implements IdentifierService {
         return body;
     }
 
-    @Override
-    @Transactional(readOnly = true)
-    public InputStreamResource exportResource(Identifier identifier) throws DataServiceException,
-            DataServiceConnectionException, QueryNotFoundException {
-        final ExportResourceDto exportResource = dataServiceGateway.exportQuery(identifier.getDatabase().getId(), identifier.getQueryId());
-        return exportResource.getResource();
-    }
-
     @Override
     @Transactional
     public void delete(Identifier identifier) throws DataServiceException, DataServiceConnectionException,
diff --git a/dbrepo-search-service/Pipfile.lock b/dbrepo-search-service/Pipfile.lock
index ac2d0e3f70431e171ffda6524d193727056840b4..e520c5d0f8890461117ca6d7ab6645de932c2261 100644
--- a/dbrepo-search-service/Pipfile.lock
+++ b/dbrepo-search-service/Pipfile.lock
@@ -373,7 +373,7 @@
         },
         "dbrepo": {
             "hashes": [
-                "sha256:4f5ee48e9a68a44c2d1184923b90729d724553862742738d8f942273a586eb3d"
+                "sha256:e70ea4f7030191eb80116e5d0a4b17b041c94c80359d5d8e707d62218edd9a54"
             ],
             "path": "./lib/dbrepo-1.7.1.tar.gz"
         },
diff --git a/dbrepo-search-service/init/Pipfile.lock b/dbrepo-search-service/init/Pipfile.lock
index 39a52462e0daa22c8052517b0617cb350e9a9408..7e637809c29b2223d18dc64896ae84432efbf7c5 100644
--- a/dbrepo-search-service/init/Pipfile.lock
+++ b/dbrepo-search-service/init/Pipfile.lock
@@ -259,7 +259,7 @@
         },
         "dbrepo": {
             "hashes": [
-                "sha256:4f5ee48e9a68a44c2d1184923b90729d724553862742738d8f942273a586eb3d"
+                "sha256:e70ea4f7030191eb80116e5d0a4b17b041c94c80359d5d8e707d62218edd9a54"
             ],
             "path": "./lib/dbrepo-1.7.1.tar.gz"
         },
diff --git a/dbrepo-search-service/init/lib/dbrepo-1.7.1-py3-none-any.whl b/dbrepo-search-service/init/lib/dbrepo-1.7.1-py3-none-any.whl
index 8e77c6e1f6a01b3d9739280f29539985c359d2c8..61f52896c18ecbec8090177b38b8dbaeb0e1a95e 100644
Binary files a/dbrepo-search-service/init/lib/dbrepo-1.7.1-py3-none-any.whl and b/dbrepo-search-service/init/lib/dbrepo-1.7.1-py3-none-any.whl differ
diff --git a/dbrepo-search-service/init/lib/dbrepo-1.7.1.tar.gz b/dbrepo-search-service/init/lib/dbrepo-1.7.1.tar.gz
index ede846d6168615c3e3bac49872362163b0a9f005..6708e1d892771d6cdf9293a6e9f5197f4dd9e304 100644
Binary files a/dbrepo-search-service/init/lib/dbrepo-1.7.1.tar.gz and b/dbrepo-search-service/init/lib/dbrepo-1.7.1.tar.gz differ
diff --git a/dbrepo-search-service/lib/dbrepo-1.7.1-py3-none-any.whl b/dbrepo-search-service/lib/dbrepo-1.7.1-py3-none-any.whl
index 8e77c6e1f6a01b3d9739280f29539985c359d2c8..61f52896c18ecbec8090177b38b8dbaeb0e1a95e 100644
Binary files a/dbrepo-search-service/lib/dbrepo-1.7.1-py3-none-any.whl and b/dbrepo-search-service/lib/dbrepo-1.7.1-py3-none-any.whl differ
diff --git a/dbrepo-search-service/lib/dbrepo-1.7.1.tar.gz b/dbrepo-search-service/lib/dbrepo-1.7.1.tar.gz
index ede846d6168615c3e3bac49872362163b0a9f005..6708e1d892771d6cdf9293a6e9f5197f4dd9e304 100644
Binary files a/dbrepo-search-service/lib/dbrepo-1.7.1.tar.gz and b/dbrepo-search-service/lib/dbrepo-1.7.1.tar.gz differ
diff --git a/dbrepo-ui/composables/query-service.ts b/dbrepo-ui/composables/query-service.ts
index 5a86c09d1b1b7017e362a62c0cc5df33c545fd6c..2b33fa8849c6d7bba4509720c2083f4d65562ff1 100644
--- a/dbrepo-ui/composables/query-service.ts
+++ b/dbrepo-ui/composables/query-service.ts
@@ -64,7 +64,7 @@ export const useQueryService = (): any => {
     }
     console.debug('export query with id', queryId, 'in database with id', databaseId)
     return new Promise<any>((resolve, reject) => {
-      axios.get<any>(`/api/database/${databaseId}/subset/${queryId}`, config)
+      axios.get<any>(`/api/database/${databaseId}/subset/${queryId}/data`, config)
         .then((response) => {
           console.info('Exported query with id', queryId, 'in database with id', databaseId)
           resolve(response.data)
diff --git a/dbrepo-ui/composables/table-service.ts b/dbrepo-ui/composables/table-service.ts
index 45ee0a7b934cb92c2d275b3cb1a23738d4fc0809..c9dfeb62fcd42c603ef86d67b52332fea04ac2a2 100644
--- a/dbrepo-ui/composables/table-service.ts
+++ b/dbrepo-ui/composables/table-service.ts
@@ -132,7 +132,7 @@ export const useTableService = (): any => {
     }
     console.debug('export data for table with id', tableId, 'in database with id', databaseId);
     return new Promise<QueryResultDto>((resolve, reject) => {
-      axios.get<QueryResultDto>(`/api/database/${databaseId}/table/${tableId}/export`, config)
+      axios.get<QueryResultDto>(`/api/database/${databaseId}/table/${tableId}/data`, config)
         .then((response) => {
           console.info('Exported data for table')
           resolve(response.data)
diff --git a/dbrepo-ui/composables/view-service.ts b/dbrepo-ui/composables/view-service.ts
index 9029a553ac782248d33707380f2ed72efcfc8032..3e775e2b14d51b83c7332bf7311de99a7a25d24e 100644
--- a/dbrepo-ui/composables/view-service.ts
+++ b/dbrepo-ui/composables/view-service.ts
@@ -114,7 +114,7 @@ export const useViewService = (): any => {
     }
     console.debug('export data for view with id', viewId, 'in database with id', databaseId);
     return new Promise<QueryResultDto>((resolve, reject) => {
-      axios.get<QueryResultDto>(`/api/database/${databaseId}/view/${viewId}/export`, config)
+      axios.get<QueryResultDto>(`/api/database/${databaseId}/view/${viewId}/data`, config)
         .then((response) => {
           console.info('Exported data for view with id', viewId, 'in database with id', databaseId)
           resolve(response.data)
diff --git a/docker-compose.yml b/docker-compose.yml
index e8c5cd8de7795d1bcced37da11b80b94dc404315..84905a7d057f6ea27dae62f7402eaa53f0d92cb7 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -539,6 +539,7 @@ services:
       AUTH_SERVICE_CLIENT: "${AUTH_SERVICE_CLIENT:-dbrepo-client}"
       AUTH_SERVICE_CLIENT_SECRET: "${AUTH_SERVICE_CLIENT:-MUwRc7yfXSJwX8AdRMWaQC3Nep1VjwgG}"
       AUTH_SERVICE_ENDPOINT: "${AUTH_SERVICE_ENDPOINT:-http://auth-service:8080}"
+      BASE_URL: "${BASE_URL:-http://localhost}"
       BROKER_EXCHANGE_NAME: "${BROKER_EXCHANGE_NAME:-dbrepo}"
       BROKER_QUEUE_NAME: "${BROKER_QUEUE_NAME:-dbrepo}"
       BROKER_HOST: "${BROKER_ENDPOINT:-broker-service}"
diff --git a/helm/dbrepo/files/create-event-listener.jar b/helm/dbrepo/files/create-event-listener.jar
index b117c50b6646de91273c95dc5749cd54868012be..0410b9e37b8fedf3956c870e2faf7aa9e8ab89b6 100644
Binary files a/helm/dbrepo/files/create-event-listener.jar and b/helm/dbrepo/files/create-event-listener.jar differ
diff --git a/lib/python/README.md b/lib/python/README.md
index 11506a7bf059630b65ed703d4674cde8cf3642e9..1a15df15c79acd1dba9ea26ab14c09cbb2419927 100644
--- a/lib/python/README.md
+++ b/lib/python/README.md
@@ -44,6 +44,7 @@ client = RestClient(endpoint="https://test.dbrepo.tuwien.ac.at", username="foo",
                     password="bar")
 df = pd.DataFrame(data={'x_coord': 16.52617, 'component': 'Feinstaub (PM10)',
                         'unit': 'µg/m³', ...})
+df = df.set_index(['x_coord'])
 client.create_table(database_id="e0d82287-9099-4077-8f69-3c19fc3bc145",
                     name="Sensor", is_public=True, is_schema_public=True,
                     dataframe=df)
@@ -51,6 +52,14 @@ client.create_table(database_id="e0d82287-9099-4077-8f69-3c19fc3bc145",
 
 ... or just create the table schema by setting `create_table(..., withData=False)`.
 
+In both cases it is important to set the index to existing columns that uniquely
+identify a row. You can specify multiple columns:
+
+```python
+...
+df = df.set_index(['some_column', 'some_other_column'])
+```
+
 ## Supported Features & Best-Practices
 
 - Manage user
diff --git a/lib/python/dbrepo/RestClient.py b/lib/python/dbrepo/RestClient.py
index f2a93151ecff15ece92618ddf30d9d7bfa0f1dcd..3459543d838cc76ee125f2ad84165caf9bced574 100644
--- a/lib/python/dbrepo/RestClient.py
+++ b/lib/python/dbrepo/RestClient.py
@@ -826,7 +826,7 @@ class RestClient:
         if page is not None and size is not None:
             params.append(('page', page))
             params.append(('size', size))
-        response = self._wrapper(method="get", url=url, params=params)
+        response = self._wrapper(method="get", url=url, params=params, headers={'Accept': 'application/json'})
         if response.status_code == 200:
             return DataFrame.from_records(response.json())
         if response.status_code == 400:
@@ -869,7 +869,7 @@ class RestClient:
             params.append(('size', size))
         if timestamp is not None:
             params.append(('timestamp', timestamp.strftime("%Y-%m-%dT%H:%M:%SZ")))
-        response = self._wrapper(method="get", url=url, params=params)
+        response = self._wrapper(method="get", url=url, params=params, headers={'Accept': 'application/json'})
         if response.status_code == 200:
             return DataFrame.from_records(response.json())
         if response.status_code == 400:
@@ -1393,11 +1393,10 @@ class RestClient:
         :raises ServiceError: If something went wrong with obtaining the information in the data service.
         :raises ResponseCodeError: If something went wrong with the retrieval.
         """
-        headers = {}
         url = f'/api/database/{database_id}/subset/{subset_id}/data'
         if page is not None and size is not None:
             url += f'?page={page}&size={size}'
-        response = self._wrapper(method="get", url=url, headers=headers)
+        response = self._wrapper(method="get", url=url, headers={'Accept': 'application/json'})
         if response.status_code == 200:
             return DataFrame.from_records(response.json())
         if response.status_code == 400:
@@ -1786,7 +1785,7 @@ class RestClient:
 
     def get_identifier(self, identifier_id: str) -> Identifier:
         """
-        Get list of identifiers, filter by the remaining optional arguments.
+        Get the identifier by given id.
 
         :param identifier_id: The identifier id.
 
@@ -1805,6 +1804,39 @@ class RestClient:
         raise ResponseCodeError(f'Failed to get identifier: response code: {response.status_code} is not '
                                 f'200 (OK): {response.text}')
 
+    def get_identifier_data(self, identifier_id: str) -> DataFrame:
+        """
+        Get the identifier data by given id.
+
+        :param identifier_id: The identifier id.
+
+        :returns: The identifier, if successful.
+
+        :raises NotExistsError: If the identifier does not exist.
+        :raises ResponseCodeError: If something went wrong with the retrieval of the identifier.
+        """
+        url = f'/api/identifier/{identifier_id}'
+        response = self._wrapper(method="get", url=url, headers={'Accept': 'application/json'})
+        if response.status_code == 200:
+            body = response.json()
+            identifier = Identifier.model_validate(body)
+            if identifier.type == IdentifierType.VIEW:
+                return self.get_view_data(database_id=identifier.database_id, view_id=identifier.view_id, page=0,
+                                          size=10000)
+            elif identifier.type == IdentifierType.TABLE:
+                return self.get_table_data(database_id=identifier.database_id, table_id=identifier.table_id, page=0,
+                                           size=10000)
+            elif identifier.type == IdentifierType.SUBSET:
+                return self.get_subset_data(database_id=identifier.database_id, subset_id=identifier.query_id, page=0,
+                                            size=10000)
+            raise FormatNotAvailable(f'Failed to get identifier data: type is database')
+        if response.status_code == 404:
+            raise NotExistsError(f'Failed to get identifier data: not found')
+        if response.status_code == 406:
+            raise NotExistsError(f'Failed to get identifier data: type database')
+        raise ResponseCodeError(f'Failed to get identifier data: response code: {response.status_code} is not '
+                                f'200 (OK): {response.text}')
+
     def get_image(self, image_id: str) -> Image:
         """
         Get container image.
diff --git a/lib/python/dbrepo/api/dto.py b/lib/python/dbrepo/api/dto.py
index 526ba29bf7d4dfea5983bce99c84c2e67e8befe4..356f9b220095970886e524e8cccefdd49ea823c7 100644
--- a/lib/python/dbrepo/api/dto.py
+++ b/lib/python/dbrepo/api/dto.py
@@ -653,6 +653,7 @@ class IdentifierSave(CreateIdentifier):
 class Identifier(BaseModel):
     id: str
     database_id: str
+    links: Links
     type: IdentifierType
     owner: UserBrief
     status: IdentifierStatusType
@@ -988,6 +989,12 @@ class Query(BaseModel):
     identifiers: List[IdentifierBrief] = field(default_factory=list)
 
 
+class Links(BaseModel):
+    self: str
+    self_html: str
+    data: Optional[str] = None
+
+
 class UpdateQuery(BaseModel):
     persist: bool