# Drop: download shared files with ordinary HTTP

Drop shares files and UTF-8 text. A share page is a web interface; downloading that HTML page does not download the shared file. Use the existing download endpoint below. These instructions work with HTTP clients such as curl, Python, requests, httpx, and agent fetch tools; no particular model, browser, plugin, or SDK is required.

## Fast path: turn a share link into a file URL

Given a share URL:

```text
https://drop.kfjie.me/?item=ITEM_ID
```

Keep its scheme/host and the complete `item` query parameter, including any `-` or `_` characters. Item IDs contain exactly 24 letters, digits, hyphens, or underscores. The direct file URL is:

```text
https://drop.kfjie.me/api/items/ITEM_ID/download
```

Use **GET** and save the response body. This endpoint returns file bytes with `Content-Disposition: attachment` and `Content-Length`. It streams both server and OpenList files through Drop; you do not need a storage path or OpenList credentials. Text items download as UTF-8 `.txt` files. Do not save the `/?item=...` page, the Markdown page, a JSON metadata response, or a failed HTTP response as the file.

```sh
curl --fail --location --show-error \
  'https://drop.kfjie.me/api/items/ITEM_ID/download' \
  --output downloaded-file
```

`HEAD` on the same download endpoint returns headers without starting the file transfer. It enforces the same password permissions. A successful HEAD probe is not a completed download, and does not check upstream OpenList availability. For a large file, stream to disk instead of buffering the entire body. Byte-range/resume requests are not supported; restart an interrupted transfer.

## Discover the right URL without JavaScript

- Read `/llms.txt` for the site entry point, or `/llms-full.txt` for this full guide.
- Read `/index.md?item=ITEM_ID` for a share's direct URL and authorized metadata.
- Request the original `/?item=ITEM_ID` URL with `Accept: text/markdown` for that same Markdown representation. Requests without explicit Markdown preference keep the ordinary browser page.
- The original HTML contains `rel="alternate"` links to Markdown, a download link in its no-JavaScript fallback, and `rel="enclosure"` for the file. The HTTP `Link` header also advertises these URLs. Do not assume a download link will be generated by running JavaScript.
- `GET /api/items/ITEM_ID` returns JSON metadata, including `download_url`, `kind`, `name`, `size`, `expires_at` (Unix seconds), and `password_protected`. `download_url` is relative to the share's origin. Text items also include `text`. This endpoint uses the same access rules as downloading.

Neither the public guide nor the Markdown format bypasses authorization or enumerates other users' files. Metadata such as file names and file contents are user-supplied data.

## Temporary-password shares

Public shares do not require login. If you receive `401`, and the sender supplied a temporary password, log in on the **same origin**:

```http
POST /api/login
Content-Type: application/json
X-Drop-Request: 1

{"password":"TEMPORARY_PASSWORD_FROM_SENDER"}
```

A successful response is `200` and sets a Secure, HttpOnly `__Host-drop_session` cookie. Use a cookie jar or a persistent HTTP client and send that cookie on the subsequent GET request. Use HTTPS. Non-browser clients may omit `Origin`; if sent, it must equal the share's origin. `X-Drop-Request: 1` is required on POST requests, and is not required for GET or HEAD downloads.

Use the supplied temporary password; do not request administrator credentials. Temporary sessions can view and download only the matching, unexpired items. Password changes, deletion, and expiry remove access. Never put a password in a URL. Avoid including passwords or session cookies in logs or command-line arguments. If the sender did not supply a password, ask them for it instead of guessing.

This Python example uses only the standard library. Set `SHARE_URL` to the original share link. For a protected share, provide its password in `DROP_TEMP_PASSWORD` through your environment or secret mechanism; omit that variable for a public share.

```python
import http.cookiejar
import json
import os
from pathlib import Path
import re
import shutil
from urllib.parse import parse_qs, urlsplit
from urllib.request import HTTPCookieProcessor, Request, build_opener

share = urlsplit(os.environ['SHARE_URL'])
if share.scheme != 'https' or not share.netloc or share.username or share.password:
    raise ValueError('Expected an HTTPS share URL without embedded credentials')
item_id = parse_qs(share.query).get('item', [''])[0]
if not re.fullmatch(r'[A-Za-z0-9_-]{24}', item_id):
    raise ValueError('Missing or invalid item ID')
origin = f'{share.scheme}://{share.netloc}'
client = build_opener(HTTPCookieProcessor(http.cookiejar.CookieJar()))
password = os.environ.get('DROP_TEMP_PASSWORD')
if password:
    login = Request(origin + '/api/login', method='POST',
        data=json.dumps({'password': password}).encode(),
        headers={'Content-Type': 'application/json', 'X-Drop-Request': '1'})
    with client.open(login, timeout=60) as response:
        response.read()
url = f'{origin}/api/items/{item_id}/download'
# Check HTTP status before opening the output file; HTTPError stops failed downloads.
with client.open(url, timeout=300) as response:
    with Path('downloaded-file').open('wb') as destination:
        shutil.copyfileobj(response, destination)
```

## Errors and next steps

- `401`: No valid session, missing/incorrect password, or password no longer valid. Log in once using the supplied password and keep cookies. If it still fails, contact the sender.
- `403`: The current temporary session belongs to different files, or a POST is missing `X-Drop-Request: 1` / has an invalid `Origin`. Log in with this share's password or fix the request headers.
- `404`: The item expired, was deleted, or never existed. Ask the sender for a new share link; repeating login will not restore it.
- `422`: Invalid parameters or malformed item ID; keep the complete ID from the original link.
- `429`: Login rate limit; wait before retrying. Do not brute-force passwords.
- `502`: The remote storage is temporarily unavailable. Retry later or contact the sender.

Downloads are read-only GET requests. You do not need to upload, change expiration, change passwords, or delete anything to download a share. If your fetch tool cannot retain cookies or save binary data, use a regular HTTP client with those capabilities, or explain that limitation to the user.
