### Build and start Sail environment Source: https://docs.speedtest-tracker.dev/contributing/development-environment.md Build the development container and start the services in detached mode. ```bash ./vendor/bin/sail build --no-cache # or if you have a Sail alias setup... sail build --no-cache ``` ```bash ./vendor/bin/sail up -d # or if you have a Sail alias setup... sail up -d ``` -------------------------------- ### Install NPM assets Source: https://docs.speedtest-tracker.dev/contributing/development-environment.md Install frontend dependencies and build assets. ```bash ./vendor/bin/sail npm install && ./vendor/bin/sail npm run build # or if you have a Sail alias setup... sail npm install && sail npm run build ``` -------------------------------- ### Install Composer dependencies Source: https://docs.speedtest-tracker.dev/contributing/development-environment.md Use a temporary Docker container to install PHP dependencies. ```bash docker run --rm \ -u "$(id -u):$(id -g)" \ -v "$(pwd):/var/www/html" \ -w /var/www/html \ laravelsail/php83-composer:latest \ composer install --ignore-platform-reqs ``` -------------------------------- ### Query documentation via HTTP GET Source: https://docs.speedtest-tracker.dev/llms.txt Perform a GET request to the documentation index using the ask and goal parameters to receive a direct answer. ```http GET https://docs.speedtest-tracker.dev/api/responses/stats.md?ask=&goal= ``` -------------------------------- ### Process queue jobs Source: https://docs.speedtest-tracker.dev/contributing/development-environment.md Start a worker process to handle background tasks like speedtests and notifications. ```bash ./vendor/bin/sail artisan queue:work # or if you have a Sail alias setup... sail artisan queue:work ``` -------------------------------- ### GET /api/v1/speedtests/list-servers Source: https://docs.speedtest-tracker.dev/api/responses/speedtests.md Retrieves a list of available Ookla speedtest servers. ```APIDOC ## GET /api/v1/speedtests/list-servers ### Description List available Ookla speedtest servers. ### Method GET ### Endpoint /api/v1/speedtests/list-servers ### Parameters #### Header Parameters - **Accept** (string) - Required - Must be "application/json" - this API only accepts and returns JSON ### Response #### Success Response (200) - **data** (array) - List of server objects - **message** (string) - Response status message #### Error Response (401) - **message** (string) - Unauthenticated error message #### Error Response (403) - **message** (string) - Error message indicating lack of permission #### Error Response (406) - **message** (string) - Error message - **error** (string) - Error description ``` -------------------------------- ### Configure Prometheus Scrape Job Source: https://docs.speedtest-tracker.dev/settings/data-platforms/prometheus.md Example configuration for a Prometheus scrape job targeting the Speedtest Tracker instance. ```yaml scrape_configs: - job_name: 'speedtest-tracker' scrape_interval: 60s # Adjust to your set schedule scrape_timeout: 10s metrics_path: /prometheus static_configs: - targets: ['speedtest-tracker.local'] ``` -------------------------------- ### GET /api/v1/ookla/list-servers Source: https://docs.speedtest-tracker.dev/api/responses/servers.md Retrieves an array of available Ookla speedtest servers. Requires an API token with the 'ookla:list-servers' scope. ```APIDOC ## GET /api/v1/ookla/list-servers ### Description Returns an array of available Ookla speedtest servers. Requires an API token with `ookla:list-servers` scope. ### Method GET ### Endpoint /api/v1/ookla/list-servers ### Parameters #### Header Parameters - **Accept** (string) - Required - Must be "application/json" - this API only accepts and returns JSON ### Response #### Success Response (200) - **data** (array) - List of server objects - **message** (string) - Response status message #### Error Responses - **401** (Unauthenticated) - User is not authenticated - **403** (Forbidden) - User lacks permission - **406** (Not Acceptable) - Missing or invalid Accept header ``` -------------------------------- ### GET /api/v1/results Source: https://docs.speedtest-tracker.dev/api/responses/results.md Retrieves a list of speedtest results. ```APIDOC ## GET /api/v1/results ### Description Retrieves speedtest results including ISP information, ping statistics, download/upload metrics, and interface details. ### Method GET ### Endpoint /api/v1/results ### Response #### Success Response (200) - **isp** (string) - ISP name - **ping** (object) - Ping statistics (low, high, jitter, latency) - **type** (string) - Result type - **result** (object) - Result metadata (id, url, persisted) - **server** (object) - Server details (id, ip, host, name, port, country, location) - **upload** (object) - Upload metrics (bytes, elapsed, latency, bandwidth) - **download** (object) - Download metrics (bytes, elapsed, latency, bandwidth) - **interface** (object) - Interface details (name, isVpn, macAddr, externalIp, internalIp) - **timestamp** (string) - ISO 8601 timestamp - **packetLoss** (number) - Packet loss percentage - **created_at** (string) - Creation timestamp - **updated_at** (string) - Update timestamp ``` -------------------------------- ### GET /api/healthcheck Source: https://docs.speedtest-tracker.dev/other/health-check.md Checks if the application is up and running. Returns a 200 status code and a confirmation message. ```APIDOC ## GET /api/healthcheck ### Description Verifies that the application is operational. ### Method GET ### Endpoint /api/healthcheck ### Response #### Success Response (200) - **message** (string) - Confirmation that Speedtest Tracker is running. #### Response Example { "message": "Speedtest Tracker is running!" } ``` -------------------------------- ### Gmail SMTP Configuration Source: https://docs.speedtest-tracker.dev/settings/notifications/mail.md Example configuration for using Gmail as the SMTP provider. Requires an App Password generated via Google account security settings. ```text MAIL_MAILER=smtp MAIL_HOST=smtp.gmail.com MAIL_PORT=465 MAIL_USERNAME="username@gmail.com" MAIL_PASSWORD="password" MAIL_FROM_ADDRESS="username@gmail.com" MAIL_FROM_NAME="Speedtest Tracker" ``` -------------------------------- ### GET /api/responses/stats.md Source: https://docs.speedtest-tracker.dev/llms.txt Queries the documentation index with a specific question and an optional end goal to receive a direct answer and relevant documentation excerpts. ```APIDOC ## GET /api/responses/stats.md ### Description Perform an HTTP GET request on the documentation index to ask a question and receive a direct answer along with relevant excerpts and sources. ### Method GET ### Endpoint https://docs.speedtest-tracker.dev/api/responses/stats.md ### Parameters #### Query Parameters - **ask** (string) - Required - The immediate question to ask, written in natural language. - **goal** (string) - Optional - Describes the broader end goal to tailor the answer. ### Request Example GET https://docs.speedtest-tracker.dev/api/responses/stats.md?ask=How+do+I+configure+the+database?&goal=Setting+up+a+new+instance ``` -------------------------------- ### Webhook Payload Examples Source: https://docs.speedtest-tracker.dev/settings/notifications/webhook.md JSON structures representing the data sent to webhooks for threshold failures and completed tests. ```json { "result_id": 14, "site_name": "Speedtest Tracker", "isp": "Speedy Communications", "benchmarks": { "download": { "bar": "min", "passed": false, "type": "absolute", "test_value": 1022, "benchmark_value": 2000, "unit": "mbps" }, "upload": { "bar": "min", "passed": false, "type": "absolute", "test_value": 1018, "benchmark_value": 2000, "unit": "mbps" }, "ping": { "bar": "max", "passed": false, "type": "absolute", "test_value": 3, "benchmark_value": 1, "unit": "ms" } }, "speedtest_url": "https://www.speedtest.net/result/c/1433a2de-eb3c-4a0e-ab29-xxxxxx", "url": "http://192.168.1.5/admin/results" } ``` ```json { "result_id": 17, "site_name": "Speedtest Tracker", "server_name": "Speedtest", "server_id": 52365, "status": "completed", "isp": "Speedy Communications", "ping": 3, "download": 1026, "upload": 1012, "packet_loss": 0, "speedtest_url": "https://www.speedtest.net/result/c/288aa4aa-a52e-493c-8d60-xxxx", "url": "http://192.168.1.5/admin/results" } ``` -------------------------------- ### GET /api/v1/results Source: https://docs.speedtest-tracker.dev/api/responses/results.md Retrieve a paginated list of speedtest results. Requires an API token with the results:read scope. ```APIDOC ## GET /api/v1/results ### Description List all speedtest results. This endpoint supports pagination, filtering by various metrics (ping, download, upload, status), and sorting. ### Method GET ### Endpoint /api/v1/results ### Parameters #### Query Parameters - **per.page** (integer) - Optional - Number of results per page (default: 25, max: 500) - **filter[ping]** (number) - Optional - Filter by ping value - **filter[download]** (integer) - Optional - Filter by download speed - **filter[upload]** (integer) - Optional - Filter by upload speed - **filter[healthy]** (boolean) - Optional - Filter by healthy status - **filter[status]** (string) - Optional - Filter by status - **filter[scheduled]** (boolean) - Optional - Filter by scheduled status - **filter[start_at]** (string) - Optional - Filter results created on or after this date - **filter[end_at]** (string) - Optional - Filter results created on or before this date - **sort** (string) - Optional - Sort results by field (prefix with - for descending). Options: ping, download, upload, created_at, updated_at ### Response #### Success Response (200) - **data** (array) - Array of result objects - **links** (object) - Pagination links - **meta** (object) - Pagination metadata ``` -------------------------------- ### GET /api/v1/results/latest Source: https://docs.speedtest-tracker.dev/api/responses/results.md Retrieves the most recent speedtest result. Requires an API token with the results:read scope. ```APIDOC ## GET /api/v1/results/latest ### Description Get the most recent result from the speedtest tracker. ### Method GET ### Endpoint /api/v1/results/latest ### Parameters #### Header Parameters - **Accept** (string) - Required - Must be "application/json" ### Response #### Success Response (200) - **id** (integer) - Result ID - **service** (string) - Service name - **ping** (number) - Ping value - **download** (integer) - Download speed - **upload** (integer) - Upload speed - **created_at** (string) - Timestamp of creation - **updated_at** (string) - Timestamp of update #### Error Responses - **401** (Unauthenticated) - User is not authenticated - **403** (Forbidden) - User lacks required permissions - **404** (Not Found) - No result found - **406** (Not Acceptable) - Missing or invalid Accept header ``` -------------------------------- ### OpenAPI Specification for GET /api/v1/results/latest Source: https://docs.speedtest-tracker.dev/api/responses/results.md Defines the endpoint for fetching the latest speedtest result, including required headers and potential error responses. ```json {"openapi":"3.0.0","info":{"title":"Speedtest Tracker API","version":"1.0.0"},"tags":[{"name":"Results","description":"Endpoints for accessing and filtering speedtest results. Requires API token with `results:read` scope."}],"paths":{"/api/v1/results/latest":{"get":{"tags":["Results"],"summary":"Get the most recent result","operationId":"getLatestResult","parameters":[{"$ref":"#/components/parameters/AcceptHeader"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthenticatedError"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"No result found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"406":{"description":"Not Acceptable - Missing or invalid Accept header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotAcceptableError"}}}}}}}},"components":{"parameters":{"AcceptHeader":{"name":"Accept","in":"header","description":"Must be \"application/json\" - this API only accepts and returns JSON","required":true,"schema":{"type":"string","default":"application/json"}}},"schemas":{"Result":{"description":"Speedtest result entry","properties":{"id":{"type":"integer"},"service":{"type":"string"},"ping":{"type":"number"},"download":{"type":"integer"},"upload":{"type":"integer"},"download_bits":{"type":"integer"},"upload_bits":{"type":"integer"},"download_bits_human":{"type":"string"},"upload_bits_human":{"type":"string"},"benchmarks":{"type":"array","items":{"type":"object"},"nullable":true},"healthy":{"type":"boolean","nullable":true},"status":{"type":"string"},"scheduled":{"type":"boolean"},"comments":{"type":"string","nullable":true},"data":{"description":"Nested speedtest data payload","properties":{"isp":{"type":"string"},"ping":{"properties":{"low":{"type":"number","format":"float"},"high":{"type":"number","format":"float"},"jitter":{"type":"number","format":"float"},"latency":{"type":"number","format":"float"}},"type":"object"},"type":{"type":"string"},"result":{"properties":{"id":{"type":"string"},"url":{"type":"string","format":"uri"},"persisted":{"type":"boolean"}},"type":"object"},"server":{"properties":{"id":{"type":"integer"},"ip":{"type":"string","format":"ipv4"},"host":{"type":"string"},"name":{"type":"string"},"port":{"type":"integer"},"country":{"type":"string"},"location":{"type":"string"}},"type":"object"},"upload":{"properties":{"bytes":{"type":"integer"},"elapsed":{"type":"integer"},"latency":{"properties":{"iqm":{"type":"number","format":"float"},"low":{"type":"number","format":"float"},"high":{"type":"number","format":"float"},"jitter":{"type":"number","format":"float"}},"type":"object"},"bandwidth":{"type":"integer"}},"type":"object"},"download":{"properties":{"bytes":{"type":"integer"},"elapsed":{"type":"integer"},"latency":{"properties":{"iqm":{"type":"number","format":"float"},"low":{"type":"number","format":"float"},"high":{"type":"number","format":"float"},"jitter":{"type":"number","format":"float"}},"type":"object"},"bandwidth":{"type":"integer"}},"type":"object"},"interface":{"properties":{"name":{"type":"string"},"isVpn":{"type":"boolean"},"macAddr":{"type":"string","pattern":"^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$"},"externalIp":{"type":"string","format":"ipv4"},"internalIp":{"type":"string","format":"ipv4"}},"type":"object"},"timestamp":{"type":"string","format":"date-time"},"packetLoss":{"type":"number"}},"type":"object"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"type":"object","additionalProperties":false},"UnauthenticatedError":{"description":"Error when user is not authenticated","properties":{"message":{"description":"Unauthenticated error message","type":"string"}},"type":"object"},"ForbiddenError":{"description":"Forbidden error response when user lacks permission","properties":{"message":{"description":"Error message indicating lack of permission","type":"string"}},"type":"object"},"NotFoundError":{"description":"Error when a requested result is not found","properties":{"message":{"description":"Result not found error message","type":"string"}},"type":"object"},"NotAcceptableError":{"description":"Error response when the Accept header is missing or invalid","properties":{"message":{"type":"string"},"error":{"type":"string"}},"type":"object"}}}} ``` -------------------------------- ### Initialize database Source: https://docs.speedtest-tracker.dev/contributing/development-environment.md Create the SQLite database file and run migrations to set up the schema. ```bash touch database/database.sqlite ``` ```bash ./vendor/bin/sail artisan migrate:fresh --force # or if you have a Sail alias setup... sail artisan migrate:fresh --force ``` -------------------------------- ### Configure environment variables Source: https://docs.speedtest-tracker.dev/contributing/development-environment.md Initialize the environment file and define required application settings. ```bash cp .env.example .env ``` ```text APP_NAME="Speedtest Tracker" APP_ENV=local APP_KEY= APP_DEBUG=false APP_TIMEZONE=UTC ``` ```bash echo "base64:$(openssl rand -base64 32 2>/dev/null)" ``` -------------------------------- ### Deploy Speedtest Tracker with Docker Compose on QNAP Source: https://docs.speedtest-tracker.dev/getting-started/installation/using-qnap.md Use this configuration in the Container Station 'Create' application dialog to set up the application and a MariaDB database. Ensure the volume paths are updated to match your local storage configuration. ```yaml version: '3.4' services: speedtest-tracker: container_name: speedtest-tracker ports: - 8080:80 - 8443:443 environment: - PUID= - PGID= - DB_CONNECTION=mariadb - DB_HOST=db - DB_PORT=3306 - DB_DATABASE=speedtest_tracker - DB_USERNAME=speedy - DB_PASSWORD=password - APP_KEY= - DATETIME_FORMAT= - APP_TIMEZONE= - SPEEDTEST_SCHEDULE= # Optional - SPEEDTEST_SERVERS= # Optional volumes: - /path/to-data:/config - /path/to-custom-ssl-keys:/config/keys image: lscr.io/linuxserver/speedtest-tracker:latest networks: qnet-network: ipv4_address: 192.168.1.3 restart: unless-stopped depends_on: - db db: image: mariadb:10 networks: qnet-network: ipv4_address: 192.168.1.4 restart: always environment: - MARIADB_DATABASE=speedtest_tracker - MARIADB_USER=speedy - MARIADB_PASSWORD=password - MARIADB_RANDOM_ROOT_PASSWORD=true volumes: - speedtest-db:/var/lib/mysql networks: qnet-network: driver_opts: iface: eth0 driver: qnet ipam: driver: qnet options: iface: eth0 config: - subnet: 192.168.1.0/24 gateway: 192.168.1.1 volumes: speedtest-db: ``` -------------------------------- ### Clone the repository Source: https://docs.speedtest-tracker.dev/contributing/development-environment.md Download the project source code and enter the directory. ```bash git clone git@github.com:alexjustesen/speedtest-tracker \ && cd speedtest-tracker ``` -------------------------------- ### GET /api/v1/stats Source: https://docs.speedtest-tracker.dev/api/responses/stats.md Fetch aggregated Speedtest statistics based on optional date filters. ```APIDOC ## GET /api/v1/stats ### Description Fetch aggregated Speedtest statistics. Requires `speedtests:read` token scope. ### Method GET ### Endpoint /api/v1/stats ### Parameters #### Query Parameters - **start_at** (string, date-time) - Optional - Filter stats from this date/time (ISO 8601) - **end_at** (string, date-time) - Optional - Filter stats up to this date/time (ISO 8601) ### Response #### Success Response (200) - **total_results** (integer) - Total number of results - **avg_ping** (number, float) - Average ping - **avg_download** (number, float) - Average download speed - **avg_upload** (number, float) - Average upload speed - **min_ping** (number, float) - Minimum ping - **min_download** (number, float) - Minimum download speed - **min_upload** (number, float) - Minimum upload speed - **max_ping** (number, float) - Maximum ping - **max_download** (number, float) - Maximum download speed - **max_upload** (number, float) - Maximum upload speed ``` -------------------------------- ### Reset development environment Source: https://docs.speedtest-tracker.dev/contributing/development-environment.md Re-run migrations to reset the database state. ```bash ./vendor/bin/sail artisan migrate:fresh --force # or if you have a Sail alias setup... sail artisan migrate:fresh --force ``` -------------------------------- ### Docker Compose Configurations Source: https://docs.speedtest-tracker.dev/getting-started/installation/using-docker-compose.md Configuration templates for deploying the application with different database backends. ```yaml services: speedtest-tracker: image: lscr.io/linuxserver/speedtest-tracker:latest restart: unless-stopped container_name: speedtest-tracker ports: - 8080:80 - 8443:443 environment: - PUID= - PGID= - APP_KEY= # Required - APP_URL= # Required - DB_CONNECTION=sqlite volumes: - /path/to/data:/config - /path/to-custom-ssl-keys:/config/keys ``` ```yaml services: speedtest-tracker: image: lscr.io/linuxserver/speedtest-tracker:latest restart: unless-stopped container_name: speedtest-tracker ports: - 8080:80 - 8443:443 environment: - PUID= - PGID= - APP_KEY= # Required - APP_URL= # Required - DB_CONNECTION=mariadb - DB_HOST=db - DB_PORT=3306 - DB_DATABASE=speedtest_tracker - DB_USERNAME=speedtest_tracker - DB_PASSWORD=password volumes: - /path/to/data:/config - /path/to-custom-ssl-keys:/config/keys depends_on: - db db: image: mariadb:11 restart: always environment: - MYSQL_DATABASE=speedtest_tracker - MYSQL_USER=speedtest_tracker - MYSQL_PASSWORD=password - MYSQL_RANDOM_ROOT_PASSWORD=true volumes: - speedtest-db:/var/lib/mysql healthcheck: test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] interval: 5s retries: 3 timeout: 5s volumes: speedtest-db: ``` ```yaml services: speedtest-tracker: image: lscr.io/linuxserver/speedtest-tracker:latest restart: unless-stopped container_name: speedtest-tracker ports: - 8080:80 - 8443:443 environment: - PUID= - PGID= - APP_KEY= # Required - APP_URL= # Required - DB_CONNECTION=mysql - DB_HOST=db - DB_PORT=3306 - DB_DATABASE=speedtest_tracker - DB_USERNAME=speedtest_tracker - DB_PASSWORD=password volumes: - /path/to/data:/config - /path/to-custom-ssl-keys:/config/keys depends_on: - db db: image: mysql:8 restart: always environment: - MYSQL_DATABASE=speedtest_tracker - MYSQL_USER=speedtest_tracker - MYSQL_PASSWORD=password - MYSQL_RANDOM_ROOT_PASSWORD=true volumes: - speedtest-db:/var/lib/mysql healthcheck: test: ["CMD", "mysqladmin", "ping", "-p${MYSQL_PASSWORD}"] interval: 5s retries: 5 timeout: 5s volumes: speedtest-db: ``` -------------------------------- ### Run Speedtest Tracker Container Source: https://docs.speedtest-tracker.dev/getting-started/installation/using-docker.md Docker run commands for deploying the application with different database configurations. ```docker docker run -d --name speedtest-tracker --restart unless-stopped \ -p 8080:80 \ -p 8443:443 \ -e PUID= \ -e PGID= \ -e APP_KEY= \ -e APP_URL= \ -e DB_CONNECTION=sqlite \ -v /path/to/data:/config \ -v /path/to-custom-ssl-keys:/config/keys \ lscr.io/linuxserver/speedtest-tracker:latest ``` ```docker docker run -d --name speedtest-tracker --restart unless-stopped \ -p 8080:80 \ -p 8443:443 \ -e PUID= \ -e PGID= \ -e APP_KEY= \ -e APP_URL= \ -e DB_CONNECTION=mariadb \ -e DB_HOST= \ -e DB_PORT=3306 \ -e DB_DATABASE=speedtest_tracker \ -e DB_USERNAME= \ -e DB_PASSWORD= \ -v /path/to/data:/config \ -v /path/to-custom-ssl-keys:/config/keys \ lscr.io/linuxserver/speedtest-tracker:latest ``` ```docker docker run -d --name speedtest-tracker --restart unless-stopped \ -p 8080:80 \ -p 8443:443 \ -e PUID= \ -e PGID= \ -e APP_KEY= \ -e APP_URL= \ -e DB_CONNECTION=mysql \ -e DB_HOST= \ -e DB_PORT=3306 \ -e DB_DATABASE=speedtest_tracker \ -e DB_USERNAME= \ -e DB_PASSWORD= \ -v /path/to/data:/config \ -v /path/to-custom-ssl-keys:/config/keys \ lscr.io/linuxserver/speedtest-tracker:latest ``` ```docker docker run -d --name speedtest-tracker --restart unless-stopped \ -p 8080:80 \ -p 8443:443 \ -e PUID=1000 \ -e PGID=1000 \ -e APP_KEY= \ -e APP_URL= \ -e DB_CONNECTION=pgsql \ -e DB_HOST= \ -e DB_PORT=5432 \ -e DB_DATABASE=speedtest_tracker \ -e DB_USERNAME= \ -e DB_PASSWORD= \ -v /path/to/data:/config \ -v /path/to-custom-ssl-keys:/config/keys \ lscr.io/linuxserver/speedtest-tracker:latest ``` -------------------------------- ### Verify application status with curl Source: https://docs.speedtest-tracker.dev/other/health-check.md Use this command to manually verify that the application is responding correctly. ```bash curl APP_URL/api/healthcheck ``` -------------------------------- ### OpenAPI Specification for GET /api/v1/stats Source: https://docs.speedtest-tracker.dev/api/responses/stats.md The OpenAPI definition for the statistics endpoint, including parameters for date filtering and response schemas. ```json {"openapi":"3.0.0","info":{"title":"Speedtest Tracker API","version":"1.0.0"},"tags":[{"name":"Stats","description":"Endpoints for retrieving aggregated statistics and performance metrics. Requires `speedtests:read` token scope."}],"paths":{"/api/v1/stats":{"get":{"tags":["Stats"],"summary":"Fetch aggregated Speedtest statistics","operationId":"getStats","parameters":[{"$ref":"#/components/parameters/AcceptHeader"},{"name":"start_at","in":"query","description":"Filter stats from this date/time (ISO 8601)","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"end_at","in":"query","description":"Filter stats up to this date/time (ISO 8601)","required":false,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Statistics fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stats"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthenticatedError"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"406":{"description":"Not Acceptable - Missing or invalid Accept header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotAcceptableError"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}}},"components":{"parameters":{"AcceptHeader":{"name":"Accept","in":"header","description":"Must be \"application/json\" - this API only accepts and returns JSON","required":true,"schema":{"type":"string","default":"application/json"}}},"schemas":{"Stats":{"description":"Aggregated speedtest statistics","properties":{"total_results":{"type":"integer"},"avg_ping":{"type":"number","format":"float"},"avg_download":{"type":"number","format":"float"},"avg_upload":{"type":"number","format":"float"},"min_ping":{"type":"number","format":"float"},"min_download":{"type":"number","format":"float"},"min_upload":{"type":"number","format":"float"},"max_ping":{"type":"number","format":"float"},"max_download":{"type":"number","format":"float"},"max_upload":{"type":"number","format":"float"}},"type":"object"},"UnauthenticatedError":{"description":"Error when user is not authenticated","properties":{"message":{"description":"Unauthenticated error message","type":"string"}},"type":"object"},"ForbiddenError":{"description":"Forbidden error response when user lacks permission","properties":{"message":{"description":"Error message indicating lack of permission","type":"string"}},"type":"object"},"NotAcceptableError":{"description":"Error response when the Accept header is missing or invalid","properties":{"message":{"type":"string"},"error":{"type":"string"}},"type":"object"},"ValidationError":{"description":"Validation failed due to invalid server_id input","properties":{"message":{"description":"Validation failed due to invalid server_id input","type":"string"}},"type":"object"}}}} ``` -------------------------------- ### Execute Ookla Speedtest CLI Source: https://docs.speedtest-tracker.dev/other/speedtest-process.md Commands used to run the speedtest via the Ookla CLI, returning results in JSON format. ```bash speedtest -accept-license --accept-gdpr --format=json ``` ```bash speedtest -accept-license --accept-gdpr --format=json --server-id=YOURSERVERID ``` -------------------------------- ### GET /api/v1/results OpenAPI Definition Source: https://docs.speedtest-tracker.dev/api/responses/results.md The OpenAPI specification for the results listing endpoint, including query parameters for filtering and sorting. ```json {"openapi":"3.0.0","info":{"title":"Speedtest Tracker API","version":"1.0.0"},"tags":[{"name":"Results","description":"Endpoints for accessing and filtering speedtest results. Requires API token with `results:read` scope."}],"paths":{"/api/v1/results":{"get":{"tags":["Results"],"summary":"List all results","operationId":"listResults","parameters":[{"$ref":"#/components/parameters/AcceptHeader"},{"name":"per.page","in":"query","description":"Number of results per page","required":false,"schema":{"type":"integer","default":25,"maximum":500,"minimum":1}},{"name":"filter[ping]","in":"query","description":"Filter by ping value (supports operators like >=, <=, etc.)","required":false,"schema":{"type":"number"}},{"name":"filter[download]","in":"query","description":"Filter by download speed (supports operators like >=, <=, etc.)","required":false,"schema":{"type":"integer"}},{"name":"filter[upload]","in":"query","description":"Filter by upload speed (supports operators like >=, <=, etc.)","required":false,"schema":{"type":"integer"}},{"name":"filter[healthy]","in":"query","description":"Filter by healthy status","required":false,"schema":{"type":"boolean"}},{"name":"filter[status]","in":"query","description":"Filter by status","required":false,"schema":{"type":"string"}},{"name":"filter[scheduled]","in":"query","description":"Filter by scheduled status","required":false,"schema":{"type":"boolean"}},{"name":"filter[start_at]","in":"query","description":"Filter results created on or after this date (alias for created_at>=)","required":false,"schema":{"type":"string","format":"date"}},{"name":"filter[end_at]","in":"query","description":"Filter results created on or before this date (alias for created_at<=)","required":false,"schema":{"type":"string","format":"date"}},{"name":"sort","in":"query","description":"Sort results by field (prefix with - for descending)","required":false,"schema":{"type":"string","enum":["ping","-ping","download","-download","upload","-upload","created_at","-created_at","updated_at","-updated_at"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultsCollection"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthenticatedError"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"406":{"description":"Not Acceptable - Missing or invalid Accept header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotAcceptableError"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}}},"components":{"parameters":{"AcceptHeader":{"name":"Accept","in":"header","description":"Must be \"application/json\" - this API only accepts and returns JSON","required":true,"schema":{"type":"string","default":"application/json"}}},"schemas":{"ResultsCollection":{"description":"Paginated list of Speedtest results","properties":{"data":{"description":"Array of result objects","type":"array","items":{"$ref":"#/components/schemas/Result"}},"links":{"properties":{"first":{"type":"string"},"last":{"type":"string"},"prev":{"type":"string","nullable":true},"next":{"type":"string","nullable":true}},"type":"object","additionalProperties":false},"meta":{"properties":{"current_page":{"type":"integer"},"from":{"type":"integer"},"last_page":{"type":"integer"},"links":{"items":{"properties":{"active":{"type":"boolean"},"label":{"type":"string"},"url":{"nullable":true,"type":"string"}},"type":"object","additionalProperties":false},"type":"array"},"path":{"type":"string"},"per.page":{"type":"integer"},"to":{"type":"integer"},"total":{"type":"integer"}},"type":"object","additionalProperties":false}},"type":"object","additionalProperties":false},"Result":{"description":"Speedtest result entry","properties":{"benchmarks":{"items":{"type":"object"},"nullable":true,"type":"array"},"comments":{"nullable":true,"type":"string"},"data":{"description":"Nested speedtest data"},"download":{"type":"integer"},"download_bits":{"type":"integer"},"download_bits_human":{"type":"string"},"healthy":{"nullable":true,"type":"boolean"},"id":{"type":"integer"},"ping":{"type":"number"},"scheduled":{"type":"boolean"},"service":{"type":"string"},"status":{"type":"string"},"upload":{"type":"integer"},"upload_bits":{"type":"integer"},"upload_bits_human":{"type":"string"}}}}}} ``` -------------------------------- ### GET /api/v1/results/{id} Source: https://docs.speedtest-tracker.dev/api/responses/results.md Retrieves a single speedtest result entry by its ID. Requires an API token with the 'results:read' scope. ```APIDOC ## GET /api/v1/results/{id} ### Description Get a single speedtest result entry by its unique ID. ### Method GET ### Endpoint /api/v1/results/{id} ### Parameters #### Path Parameters - **id** (integer) - Required - The ID of the result #### Header Parameters - **Accept** (string) - Required - Must be "application/json" ### Response #### Success Response (200) - **data** (object) - The speedtest result entry - **message** (string) - Response status message ``` -------------------------------- ### Docker Compose Configuration Source: https://docs.speedtest-tracker.dev/getting-started/installation/using-docker-compose.md Defines the services for the application and the database, including environment variables and volume mappings. ```yaml services: speedtest-tracker: image: lscr.io/linuxserver/speedtest-tracker:latest restart: unless-stopped container_name: speedtest-tracker ports: - 8080:80 - 8443:443 environment: - PUID= - PGID= - APP_KEY= # Required - APP_URL= # Required - DB_CONNECTION=pgsql - DB_HOST=db - DB_PORT=5432 - DB_DATABASE=speedtest_tracker - DB_USERNAME=speedtest_tracker - DB_PASSWORD=password volumes: - /path/to/data:/config - /path/to-custom-ssl-keys:/config/keys depends_on: - db db: image: postgres:18 restart: always environment: - POSTGRES_DB=speedtest_tracker - POSTGRES_USER=speedtest_tracker - POSTGRES_PASSWORD=password - PGDATA=/var/lib/postgresql/data/ volumes: - speedtest-db:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"] interval: 10s retries: 5 timeout: 5s volumes: speedtest-db: ``` -------------------------------- ### OpenAPI Specification for Run Speedtest Source: https://docs.speedtest-tracker.dev/api/responses/speedtests.md Defines the POST endpoint for initiating a speedtest, including parameters and potential error responses. ```json {"openapi":"3.0.0","info":{"title":"Speedtest Tracker API","version":"1.0.0"},"tags":[{"name":"Speedtests","description":"Endpoints for running speedtests and listing servers."}],"paths":{"/api/v1/speedtests/run":{"post":{"tags":["Speedtests"],"summary":"Run a new Ookla speedtest","operationId":"runSpeedtest","parameters":[{"$ref":"#/components/parameters/AcceptHeader"},{"name":"server_id","in":"query","description":"Optional Ookla speedtest server ID","required":false,"schema":{"type":"integer"}}],"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeedtestRun"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthenticatedError"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"406":{"description":"Not Acceptable - Missing or invalid Accept header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotAcceptableError"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}}},"components":{"parameters":{"AcceptHeader":{"name":"Accept","in":"header","description":"Must be \"application/json\" - this API only accepts and returns JSON","required":true,"schema":{"type":"string","default":"application/json"}}},"schemas":{"SpeedtestRun":{"description":"A queued speedtest result","properties":{"data":{"description":"Queued speedtest result payload","properties":{"id":{"type":"integer"},"service":{"type":"string"},"ping":{"type":"number","format":"float","nullable":true},"download":{"type":"integer","nullable":true},"upload":{"type":"integer","nullable":true},"benchmarks":{"type":"object","nullable":true},"healthy":{"type":"boolean","nullable":true},"status":{"type":"string"},"scheduled":{"type":"boolean"},"comments":{"type":"string","nullable":true},"data":{"description":"Additional data for queued result","properties":{"server":{"properties":{"id":{"type":"integer","nullable":true}},"type":"object"}},"type":"object"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"type":"object","additionalProperties":false},"message":{"description":"Response status message","type":"string"}},"type":"object","additionalProperties":false},"UnauthenticatedError":{"description":"Error when user is not authenticated","properties":{"message":{"description":"Unauthenticated error message","type":"string"}},"type":"object"},"ForbiddenError":{"description":"Forbidden error response when user lacks permission","properties":{"message":{"description":"Error message indicating lack of permission","type":"string"}},"type":"object"},"NotAcceptableError":{"description":"Error response when the Accept header is missing or invalid","properties":{"message":{"type":"string"},"error":{"type":"string"}},"type":"object"},"ValidationError":{"description":"Validation failed due to invalid server_id input","properties":{"message":{"description":"Validation failed due to invalid server_id input","type":"string"}},"type":"object"}}}} ```