{"slug":"api-storage","title":"Object Storage API","description":"Manage S3-compatible object storage buckets and access keys programmatically through the DanubeData API.","section":"API Reference","url":"https://docs.danubedata.ro/api-storage","markdown_url":"https://docs.danubedata.ro/api-storage.md","breadcrumbs":[{"title":"API Reference","slug":null},{"title":"Object Storage","slug":"api-storage"}],"headings":[{"level":1,"title":"Object Storage API","id":"object-storage-api"},{"level":2,"title":"Endpoints Overview","id":"endpoints-overview"},{"level":3,"title":"Storage Buckets","id":"storage-buckets"},{"level":3,"title":"Storage Access Keys","id":"storage-access-keys"},{"level":2,"title":"Storage Buckets","id":"storage-buckets"},{"level":3,"title":"List All Buckets","id":"list-all-buckets"},{"level":3,"title":"Create a Bucket","id":"create-a-bucket"},{"level":3,"title":"Get Bucket Details","id":"get-bucket-details"},{"level":3,"title":"Update Bucket Settings","id":"update-bucket-settings"},{"level":3,"title":"Delete a Bucket","id":"delete-a-bucket"},{"level":3,"title":"Get Bucket Metrics","id":"get-bucket-metrics"},{"level":3,"title":"Get Metrics Trend","id":"get-metrics-trend"},{"level":3,"title":"Get Top Objects","id":"get-top-objects"},{"level":3,"title":"Get Bucket Health","id":"get-bucket-health"},{"level":2,"title":"Storage Access Keys","id":"storage-access-keys"},{"level":3,"title":"List All Access Keys","id":"list-all-access-keys"},{"level":3,"title":"Create an Access Key","id":"create-an-access-key"},{"level":3,"title":"Get Access Key Details","id":"get-access-key-details"},{"level":3,"title":"Revoke an Access Key","id":"revoke-an-access-key"},{"level":2,"title":"Using Access Keys with S3 Clients","id":"using-access-keys-with-s3-clients"},{"level":3,"title":"AWS CLI","id":"aws-cli"},{"level":3,"title":"Python (boto3)","id":"python-boto3"},{"level":3,"title":"JavaScript (AWS SDK v3)","id":"javascript-aws-sdk-v3"},{"level":2,"title":"Error Responses","id":"error-responses"},{"level":3,"title":"422 Unprocessable Entity","id":"422-unprocessable-entity"},{"level":3,"title":"404 Not Found","id":"404-not-found"},{"level":3,"title":"403 Forbidden","id":"403-forbidden"},{"level":2,"title":"Rate Limits","id":"rate-limits"}],"format":"markdown","word_count":1802,"content":"# Object Storage API\n\nManage S3-compatible object storage buckets and access keys programmatically through the DanubeData API.\n\n## Endpoints Overview\n\n### Storage Buckets\n\n| Method | Endpoint | Description | Scope Required |\n|--------|----------|-------------|----------------|\n| GET | `/api/v1/storage/buckets` | List all buckets | `storage:read` |\n| POST | `/api/v1/storage/buckets` | Create a new bucket | `storage:write` |\n| GET | `/api/v1/storage/buckets/{id}` | Get bucket details | `storage:read` |\n| PUT | `/api/v1/storage/buckets/{id}` | Update bucket settings | `storage:write` |\n| DELETE | `/api/v1/storage/buckets/{id}` | Delete a bucket | `storage:delete` |\n| GET | `/api/v1/storage/buckets/{id}/metrics` | Get bucket metrics | `storage:read` |\n| GET | `/api/v1/storage/buckets/{id}/metrics/trend` | Historical time-series metrics | `storage:read` |\n| GET | `/api/v1/storage/buckets/{id}/metrics/top-objects` | Top objects by size / egress / requests | `storage:read` |\n| GET | `/api/v1/storage/buckets/{id}/metrics/health` | Bucket health (multipart, deleted versions) | `storage:read` |\n\n### Storage Access Keys\n\n| Method | Endpoint | Description | Scope Required |\n|--------|----------|-------------|----------------|\n| GET | `/api/v1/storage/access-keys` | List all access keys | `storage:read` |\n| POST | `/api/v1/storage/access-keys` | Create a new access key | `storage:write` |\n| GET | `/api/v1/storage/access-keys/{id}` | Get access key details | `storage:read` |\n| DELETE | `/api/v1/storage/access-keys/{id}` | Revoke an access key | `storage:delete` |\n\n## Storage Buckets\n\n### List All Buckets\n\n```bash\ncurl -X GET 'https://danubedata.ro/api/v1/storage/buckets' \\\n  -H 'Authorization: Bearer YOUR_TOKEN' \\\n  -H 'Accept: application/json'\n```\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\n      \"id\": \"9c8b7a6e-5d4c-3b2a-1098-76543210fedc\",\n      \"name\": \"my-bucket\",\n      \"display_name\": \"My Application Bucket\",\n      \"status\": \"active\",\n      \"status_label\": \"Active\",\n      \"region\": \"fsn1\",\n      \"endpoint_url\": \"https://s3.danubedata.ro\",\n      \"public_access\": false,\n      \"versioning_enabled\": false,\n      \"encryption_enabled\": true,\n      \"size_bytes\": 1073741824,\n      \"size_human\": \"1.00 GB\",\n      \"object_count\": 150,\n      \"monthly_cost_cents\": 500,\n      \"monthly_cost_dollars\": 5.00,\n      \"created_at\": \"2024-01-15T10:30:00Z\",\n      \"updated_at\": \"2024-01-15T10:30:00Z\"\n    }\n  ],\n  \"pagination\": {\n    \"current_page\": 1,\n    \"last_page\": 1,\n    \"per_page\": 15,\n    \"total\": 1\n  }\n}\n```\n\n### Create a Bucket\n\n```bash\ncurl -X POST 'https://danubedata.ro/api/v1/storage/buckets' \\\n  -H 'Authorization: Bearer YOUR_TOKEN' \\\n  -H 'Content-Type: application/json' \\\n  -H 'Accept: application/json' \\\n  -d '{\n    \"name\": \"my-new-bucket\",\n    \"display_name\": \"My New Bucket\",\n    \"region\": \"fsn1\",\n    \"versioning_enabled\": false,\n    \"public_access\": false,\n    \"encryption_enabled\": true,\n    \"tags\": {\n      \"environment\": \"production\",\n      \"project\": \"webapp\"\n    }\n  }'\n```\n\n**Parameters:**\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `name` | string | Yes | Bucket name (3-63 chars, lowercase, alphanumeric and hyphens) |\n| `display_name` | string | No | Human-readable display name |\n| `region` | string | Yes | Region code (e.g., `fsn1`) |\n| `versioning_enabled` | boolean | No | Enable object versioning (default: false) |\n| `public_access` | boolean | No | Allow public read access (default: false) |\n| `encryption_enabled` | boolean | No | Enable server-side encryption (default: true) |\n| `encryption_type` | string | No | Encryption type: `sse-s3` or `sse-kms` |\n| `cors_configuration` | array | No | CORS rules for the bucket |\n| `lifecycle_rules` | array | No | Object lifecycle rules |\n| `tags` | object | No | Key-value tags for the bucket |\n\n**Response (201 Created):**\n```json\n{\n  \"message\": \"Storage bucket created successfully\",\n  \"bucket\": {\n    \"id\": \"9c8b7a6e-5d4c-3b2a-1098-76543210fedc\",\n    \"name\": \"my-new-bucket\",\n    \"status\": \"pending\",\n    \"status_label\": \"Pending\"\n  }\n}\n```\n\n### Get Bucket Details\n\n```bash\ncurl -X GET 'https://danubedata.ro/api/v1/storage/buckets/{bucket_id}' \\\n  -H 'Authorization: Bearer YOUR_TOKEN' \\\n  -H 'Accept: application/json'\n```\n\n**Response:**\n```json\n{\n  \"bucket\": {\n    \"id\": \"9c8b7a6e-5d4c-3b2a-1098-76543210fedc\",\n    \"name\": \"my-bucket\",\n    \"display_name\": \"My Application Bucket\",\n    \"status\": \"active\",\n    \"region\": \"fsn1\",\n    \"endpoint_url\": \"https://s3.danubedata.ro\",\n    \"public_url\": null,\n    \"public_access\": false,\n    \"versioning_enabled\": false,\n    \"encryption_enabled\": true,\n    \"encryption_type\": \"sse-s3\",\n    \"size_bytes\": 1073741824,\n    \"object_count\": 150,\n    \"tags\": {\n      \"environment\": \"production\"\n    },\n    \"monthly_cost_cents\": 500,\n    \"monthly_cost_dollars\": 5.00,\n    \"can_be_modified\": true,\n    \"can_be_destroyed\": true,\n    \"created_at\": \"2024-01-15T10:30:00Z\",\n    \"updated_at\": \"2024-01-15T10:30:00Z\"\n  },\n  \"endpoint\": \"https://s3.danubedata.ro\"\n}\n```\n\n### Update Bucket Settings\n\n```bash\ncurl -X PUT 'https://danubedata.ro/api/v1/storage/buckets/{bucket_id}' \\\n  -H 'Authorization: Bearer YOUR_TOKEN' \\\n  -H 'Content-Type: application/json' \\\n  -H 'Accept: application/json' \\\n  -d '{\n    \"display_name\": \"Updated Bucket Name\",\n    \"versioning_enabled\": true,\n    \"public_access\": false,\n    \"tags\": {\n      \"environment\": \"staging\"\n    }\n  }'\n```\n\n**Response:**\n```json\n{\n  \"message\": \"Bucket settings updated successfully\",\n  \"bucket\": {\n    \"id\": \"9c8b7a6e-5d4c-3b2a-1098-76543210fedc\",\n    \"name\": \"my-bucket\",\n    \"display_name\": \"Updated Bucket Name\",\n    \"versioning_enabled\": true\n  }\n}\n```\n\n### Delete a Bucket\n\n```bash\ncurl -X DELETE 'https://danubedata.ro/api/v1/storage/buckets/{bucket_id}' \\\n  -H 'Authorization: Bearer YOUR_TOKEN' \\\n  -H 'Accept: application/json'\n```\n\n**Response:**\n```json\n{\n  \"message\": \"Bucket deletion initiated\",\n  \"status\": \"destroying\"\n}\n```\n\n**Note:** Bucket deletion is asynchronous. The bucket and all its objects will be permanently deleted.\n\n### Get Bucket Metrics\n\n```bash\ncurl -X GET 'https://danubedata.ro/api/v1/storage/buckets/{bucket_id}/metrics' \\\n  -H 'Authorization: Bearer YOUR_TOKEN' \\\n  -H 'Accept: application/json'\n```\n\n**Response:**\n```json\n{\n  \"size_bytes\": 1073741824,\n  \"size_human\": \"1.00 GB\",\n  \"object_count\": 150,\n  \"requests_24h\": 48213,\n  \"requests_24h_by_method\": { \"GET\": 41002, \"PUT\": 6100, \"DELETE\": 1111 },\n  \"requests_24h_by_status\": { \"2xx\": 47800, \"4xx\": 390, \"5xx\": 23 },\n  \"error_rate_24h\": 0.0086,\n  \"latency_24h_ms\": { \"p50\": 12.4, \"p95\": 58.1, \"mean\": 18.7 },\n  \"egress_bytes_24h\": 5368709120,\n  \"egress_human_24h\": \"5.00 GB\",\n  \"ingress_bytes_24h\": 1073741824,\n  \"ingress_human_24h\": \"1.00 GB\",\n  \"monthly_cost_cents\": 500,\n  \"monthly_cost_dollars\": 5.00,\n  \"source\": \"deltas\",\n  \"freshness\": \"fresh\",\n  \"last_sync_at\": \"2024-01-15T12:00:00Z\",\n  \"metrics_precomputed_at\": \"2024-01-15T12:00:00Z\"\n}\n```\n\nNotes:\n- **`freshness`** is `fresh`, `lagging`, or `stale` — how recent the metrics are.\n- **`source`** is `deltas` when the per-interval metrics pipeline has data for the bucket, or `legacy` for older buckets where the status/latency/ingress fields are `null`. Skip absent dimensions when `source` is `legacy`.\n\n### Get Metrics Trend\n\nHistorical time-series for a bucket.\n\n```bash\ncurl -X GET 'https://danubedata.ro/api/v1/storage/buckets/{bucket_id}/metrics/trend?window=24h' \\\n  -H 'Authorization: Bearer YOUR_TOKEN' \\\n  -H 'Accept: application/json'\n```\n\n**Query parameters:**\n\n| Parameter | Values | Default | Description |\n|-----------|--------|---------|-------------|\n| `window` | `1h`, `6h`, `12h`, `24h`, `3d`, `7d`, `14d`, `30d` | `24h` | Time range |\n| `resolution` | `1m`, `5m`, `10m`, `15m`, `30m`, `1h`, `6h`, `1d` | auto (from `window`) | Bucket interval for each data point |\n\n**Response:**\n```json\n{\n  \"bucket_id\": \"01HXYZ...\",\n  \"window\": \"24h\",\n  \"resolution\": \"5m\",\n  \"source\": \"deltas\",\n  \"freshness\": \"fresh\",\n  \"generated_at\": \"2024-01-15T12:00:00Z\",\n  \"data\": [ ... ]\n}\n```\n\n### Get Top Objects\n\nTop-N objects in a bucket by size, egress, or request count. Powered by an hourly probe — returns an empty `items` array until the first snapshot has run.\n\n```bash\ncurl -X GET 'https://danubedata.ro/api/v1/storage/buckets/{bucket_id}/metrics/top-objects?dimension=size&limit=10' \\\n  -H 'Authorization: Bearer YOUR_TOKEN' \\\n  -H 'Accept: application/json'\n```\n\n**Query parameters:**\n\n| Parameter | Values | Default | Description |\n|-----------|--------|---------|-------------|\n| `dimension` | `size`, `egress`, `requests` | `size` | Ranking dimension |\n| `limit` | `1`–`50` | `10` | Number of objects to return |\n\n**Response:**\n```json\n{\n  \"bucket_id\": \"01HXYZ...\",\n  \"dimension\": \"size\",\n  \"recorded_at\": \"2024-01-15T11:00:00Z\",\n  \"items\": [\n    { \"rank\": 1, \"object_key\": \"backups/2024-01-15.tar.gz\", \"value\": 2147483648 },\n    { \"rank\": 2, \"object_key\": \"media/video.mp4\", \"value\": 734003200 }\n  ]\n}\n```\n\n### Get Bucket Health\n\nReclaimable-space and freshness indicators. Values are `null` until the hourly health probe has visited the bucket.\n\n```bash\ncurl -X GET 'https://danubedata.ro/api/v1/storage/buckets/{bucket_id}/metrics/health' \\\n  -H 'Authorization: Bearer YOUR_TOKEN' \\\n  -H 'Accept: application/json'\n```\n\n**Response:**\n```json\n{\n  \"bucket_id\": \"01HXYZ...\",\n  \"pending_multipart_count\": 3,\n  \"pending_multipart_bytes\": 15728640,\n  \"deleted_size_bytes\": 524288000,\n  \"freshness\": \"fresh\",\n  \"metrics_precomputed_at\": \"2024-01-15T12:00:00Z\",\n  \"last_health_check_at\": \"2024-01-15T11:00:00Z\"\n}\n```\n\n## Storage Access Keys\n\nAccess keys provide S3-compatible credentials for accessing your storage buckets programmatically.\n\n### List All Access Keys\n\n```bash\ncurl -X GET 'https://danubedata.ro/api/v1/storage/access-keys' \\\n  -H 'Authorization: Bearer YOUR_TOKEN' \\\n  -H 'Accept: application/json'\n```\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\n      \"id\": \"abc12345-6789-0abc-def1-234567890abc\",\n      \"name\": \"Production API Key\",\n      \"access_key_id\": \"DDAK1234567890EXAMPLE\",\n      \"access_key_id_masked\": \"DDAK****XAMPLE\",\n      \"status\": \"active\",\n      \"status_label\": \"Active\",\n      \"permissions\": null,\n      \"expires_at\": null,\n      \"last_used_at\": \"2024-01-15T11:30:00Z\",\n      \"is_expired\": false,\n      \"created_at\": \"2024-01-10T09:00:00Z\",\n      \"updated_at\": \"2024-01-15T11:30:00Z\"\n    }\n  ],\n  \"pagination\": {\n    \"current_page\": 1,\n    \"last_page\": 1,\n    \"per_page\": 15,\n    \"total\": 1\n  }\n}\n```\n\n### Create an Access Key\n\n```bash\ncurl -X POST 'https://danubedata.ro/api/v1/storage/access-keys' \\\n  -H 'Authorization: Bearer YOUR_TOKEN' \\\n  -H 'Content-Type: application/json' \\\n  -H 'Accept: application/json' \\\n  -d '{\n    \"name\": \"My Application Key\",\n    \"expires_at\": \"2025-01-15T00:00:00Z\"\n  }'\n```\n\n**Parameters:**\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `name` | string | Yes | A descriptive name for the access key |\n| `expires_at` | datetime | No | Expiration date (ISO 8601 format) |\n\n**Response (201 Created):**\n```json\n{\n  \"id\": \"abc12345-6789-0abc-def1-234567890abc\",\n  \"name\": \"My Application Key\",\n  \"access_key_id\": \"DDAK1234567890EXAMPLE\",\n  \"secret_access_key\": \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\",\n  \"expires_at\": \"2025-01-15T00:00:00Z\",\n  \"message\": \"Access key created. Make sure to save the secret key - it will not be shown again.\"\n}\n```\n\n**Important:** The `secret_access_key` is only returned once during creation. Store it securely as it cannot be retrieved later.\n\n### Get Access Key Details\n\n```bash\ncurl -X GET 'https://danubedata.ro/api/v1/storage/access-keys/{access_key_id}' \\\n  -H 'Authorization: Bearer YOUR_TOKEN' \\\n  -H 'Accept: application/json'\n```\n\n**Response:**\n```json\n{\n  \"access_key\": {\n    \"id\": \"abc12345-6789-0abc-def1-234567890abc\",\n    \"name\": \"My Application Key\",\n    \"access_key_id\": \"DDAK1234567890EXAMPLE\",\n    \"access_key_id_masked\": \"DDAK****XAMPLE\",\n    \"status\": \"active\",\n    \"status_label\": \"Active\",\n    \"expires_at\": \"2025-01-15T00:00:00Z\",\n    \"last_used_at\": \"2024-01-15T11:30:00Z\",\n    \"is_expired\": false,\n    \"created_at\": \"2024-01-10T09:00:00Z\"\n  }\n}\n```\n\n**Note:** The `secret_access_key` is never returned after initial creation.\n\n### Revoke an Access Key\n\n```bash\ncurl -X DELETE 'https://danubedata.ro/api/v1/storage/access-keys/{access_key_id}' \\\n  -H 'Authorization: Bearer YOUR_TOKEN' \\\n  -H 'Accept: application/json'\n```\n\n**Response:**\n```json\n{\n  \"message\": \"Access key has been revoked\"\n}\n```\n\n**Warning:** Revoking an access key immediately invalidates it. Any applications using this key will lose access.\n\n## Using Access Keys with S3 Clients\n\nOnce you have created an access key, you can use it with any S3-compatible client.\n\n### AWS CLI\n\n```bash\naws configure --profile danubedata\n\n# Enter your credentials when prompted:\n# AWS Access Key ID: DDAK1234567890EXAMPLE\n# AWS Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\n# Default region name: fsn1\n\n# Use with custom endpoint\naws s3 ls s3://my-bucket \\\n  --endpoint-url https://s3.danubedata.ro \\\n  --profile danubedata\n```\n\n### Python (boto3)\n\n```python\nimport boto3\n\ns3 = boto3.client(\n    's3',\n    endpoint_url='https://s3.danubedata.ro',\n    aws_access_key_id='DDAK1234567890EXAMPLE',\n    aws_secret_access_key='wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',\n    region_name='fsn1'\n)\n\n# List objects in a bucket\nresponse = s3.list_objects_v2(Bucket='my-bucket')\nfor obj in response.get('Contents', []):\n    print(obj['Key'])\n```\n\n### JavaScript (AWS SDK v3)\n\n```javascript\nimport { S3Client, ListObjectsV2Command } from '@aws-sdk/client-s3';\n\nconst s3 = new S3Client({\n  endpoint: 'https://s3.danubedata.ro',\n  region: 'fsn1',\n  credentials: {\n    accessKeyId: 'DDAK1234567890EXAMPLE',\n    secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'\n  },\n  forcePathStyle: true\n});\n\nconst command = new ListObjectsV2Command({ Bucket: 'my-bucket' });\nconst response = await s3.send(command);\nconsole.log(response.Contents);\n```\n\n## Error Responses\n\n### 422 Unprocessable Entity\n\n```json\n{\n  \"error\": \"You have reached the maximum number of buckets (10). Please delete a bucket to create a new one.\"\n}\n```\n\n```json\n{\n  \"error\": \"Bucket cannot be modified in its current state\",\n  \"status\": \"creating\"\n}\n```\n\n### 404 Not Found\n\n```json\n{\n  \"error\": \"Bucket not found\"\n}\n```\n\n### 403 Forbidden\n\nReturned when your API token lacks the required `storage:*` scope.\n\n```json\n{\n  \"message\": \"Insufficient permissions\"\n}\n```\n\n## Rate Limits\n\nStorage API endpoints have specific rate limits:\n\n| Endpoint | Limit |\n|----------|-------|\n| Create bucket | 40/min, 200/hour, 2000/day |\n| Create access key | 20/min, 100/hour, 1000/day |\n| Get metrics | 2x standard limits |\n| Other endpoints | Standard tier limits |\n\nSee [Rate Limits](https://docs.danubedata.ro/api-rate-limits) for more details.\n","prev":{"title":"Authentication","slug":"api-authentication","url":"https://docs.danubedata.ro/api-authentication","markdown_url":"https://docs.danubedata.ro/api-authentication.md","json_url":"https://docs.danubedata.ro/api-authentication.json"},"next":{"title":"Rate Limits","slug":"api-rate-limits","url":"https://docs.danubedata.ro/api-rate-limits","markdown_url":"https://docs.danubedata.ro/api-rate-limits.md","json_url":"https://docs.danubedata.ro/api-rate-limits.json"},"index_url":"https://docs.danubedata.ro/index.json"}