Community examples
Tide API examples for real client apps.
Start with small functions that perform one request, handle HTTP and API errors, and return the relevant WorldTides array.
Code
API examples by language
Each example keeps the integration surface small: one reusable client, one shared request method, and focused functions for tide extremes and sampled heights.
import Foundation
enum WorldTidesError: Error {
case badURL, http(Int), api(String)
}
struct TideExtreme: Codable {
let dt: Int
let date: String?
let height: Double
let type: String
}
struct TideSample: Codable {
let dt: Int
let date: String?
let height: Double
}
struct TideResponse: Codable {
let status: Int?
let error: String?
let extremes: [TideExtreme]?
let heights: [TideSample]?
}
final class WorldTides {
let key: String
let lat: Double
let lon: Double
let base = URL(string: "https://www.worldtides.info/api/v3")!
init(key: String, lat: Double, lon: Double) {
self.key = key
self.lat = lat
self.lon = lon
}
func getExtremes(year: Int, month: Int, day: Int, numDays: Int) async throws -> [TideExtreme] {
try await request(flag: "extremes", year: year, month: month, day: day, numDays: numDays).extremes ?? []
}
func getSamples(year: Int, month: Int, day: Int, numDays: Int) async throws -> [TideSample] {
try await request(flag: "heights", year: year, month: month, day: day, numDays: numDays).heights ?? []
}
private func request(flag: String, year: Int, month: Int, day: Int, numDays: Int) async throws -> TideResponse {
var parts = URLComponents(url: base, resolvingAgainstBaseURL: false)!
parts.queryItems = [
URLQueryItem(name: flag, value: nil),
URLQueryItem(name: "date", value: String(format: "%04d-%02d-%02d", year, month, day)),
URLQueryItem(name: "days", value: "\(numDays)"),
URLQueryItem(name: "lat", value: "\(lat)"),
URLQueryItem(name: "lon", value: "\(lon)"),
URLQueryItem(name: "key", value: key)
]
guard let url = parts.url else { throw WorldTidesError.badURL }
let (data, response) = try await URLSession.shared.data(from: url)
let code = (response as? HTTPURLResponse)?.statusCode ?? 0
guard (200..<300).contains(code) else { throw WorldTidesError.http(code) }
let decoded = try JSONDecoder().decode(TideResponse.self, from: data)
if let status = decoded.status, status != 200 {
throw WorldTidesError.api(decoded.error ?? "WorldTides error")
}
return decoded
}
}
@main
struct App {
static func main() async {
do {
let tides = WorldTides(key: "<apiKey>", lat: 33.768321, lon: -118.195617)
let extremes = try await tides.getExtremes(year: 2026, month: 7, day: 1, numDays: 3)
let samples = try await tides.getSamples(year: 2026, month: 7, day: 1, numDays: 3)
print("Extremes: \(extremes.count)")
print("Samples: \(samples.count)")
} catch {
print("WorldTides error: \(error)")
}
}
}
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import java.net.URI
import java.net.URLEncoder
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.nio.charset.StandardCharsets
@Serializable
data class TideExtreme(
val dt: Long,
val height: Double,
val type: String,
val date: String? = null,
)
@Serializable
data class TideSample(
val dt: Long,
val height: Double,
val date: String? = null,
)
@Serializable
data class TideResponse(
val status: Int? = null,
val error: String? = null,
val extremes: List<TideExtreme>? = null,
val heights: List<TideSample>? = null,
)
class WorldTides(private val key: String, private val lat: Double, private val lon: Double) {
private val base = "https://www.worldtides.info/api/v3"
private val client = HttpClient.newHttpClient()
private val json = Json { ignoreUnknownKeys = true }
fun getExtremes(year: Int, month: Int, day: Int, numDays: Int): List<TideExtreme> =
request("extremes", year, month, day, numDays).extremes.orEmpty()
fun getSamples(year: Int, month: Int, day: Int, numDays: Int): List<TideSample> =
request("heights", year, month, day, numDays).heights.orEmpty()
private fun request(flag: String, year: Int, month: Int, day: Int, numDays: Int): TideResponse {
val date = "%04d-%02d-%02d".format(year, month, day)
val url = "$base?$flag&date=${enc(date)}&days=$numDays&lat=$lat&lon=$lon&key=${enc(key)}"
val httpRequest = HttpRequest.newBuilder(URI.create(url)).GET().build()
val httpResponse = client.send(httpRequest, HttpResponse.BodyHandlers.ofString())
if (httpResponse.statusCode() !in 200..299) {
error("HTTP ${httpResponse.statusCode()}: ${httpResponse.body()}")
}
val decoded = json.decodeFromString<TideResponse>(httpResponse.body())
if (decoded.status != null && decoded.status != 200) {
error(decoded.error ?: "WorldTides error")
}
return decoded
}
private fun enc(value: String) =
URLEncoder.encode(value, StandardCharsets.UTF_8)
}
fun main() {
val tides = WorldTides("<apiKey>", 33.768321, -118.195617)
val extremes = tides.getExtremes(2026, 7, 1, 3)
val samples = tides.getSamples(2026, 7, 1, 3)
println("Extremes: ${extremes.size}")
println("Samples: ${samples.size}")
}
curl -fsS "https://www.worldtides.info/api/v3?extremes&date=2026-07-01&days=3&lat=33.768321&lon=-118.195617&key=<apiKey>"
curl -fsS "https://www.worldtides.info/api/v3?heights&date=2026-07-01&days=3&lat=33.768321&lon=-118.195617&key=<apiKey>"
from urllib.parse import urlencode
from urllib.request import urlopen
import json
class WorldTides:
def __init__(self, key, lat, lon):
self.key = key
self.lat = lat
self.lon = lon
self.base = "https://www.worldtides.info/api/v3"
def get_extremes(self, year, month, day, num_days):
return self._request("extremes", year, month, day, num_days).get("extremes", [])
def get_samples(self, year, month, day, num_days):
return self._request("heights", year, month, day, num_days).get("heights", [])
def _request(self, flag, year, month, day, num_days):
query = urlencode({
"date": f"{year:04d}-{month:02d}-{day:02d}",
"days": num_days,
"lat": self.lat,
"lon": self.lon,
"key": self.key,
})
with urlopen(f"{self.base}?{flag}&{query}", timeout=10) as response:
data = json.loads(response.read().decode("utf-8"))
if data.get("status", 200) != 200:
raise RuntimeError(data.get("error", "WorldTides error"))
return data
tides = WorldTides("<apiKey>", 33.768321, -118.195617)
extremes = tides.get_extremes(2026, 7, 1, 3)
samples = tides.get_samples(2026, 7, 1, 3)
<?php
class WorldTides {
private string $base = 'https://www.worldtides.info/api/v3';
public function __construct(
private string $key,
private float $lat,
private float $lon
) {}
public function getExtremes(int $year, int $month, int $day, int $numDays): array {
return $this->request('extremes', $year, $month, $day, $numDays)['extremes'] ?? [];
}
public function getSamples(int $year, int $month, int $day, int $numDays): array {
return $this->request('heights', $year, $month, $day, $numDays)['heights'] ?? [];
}
private function request(string $flag, int $year, int $month, int $day, int $numDays): array {
$query = http_build_query([
'date' => sprintf('%04d-%02d-%02d', $year, $month, $day),
'days' => $numDays,
'lat' => $this->lat,
'lon' => $this->lon,
'key' => $this->key,
]);
$body = file_get_contents($this->base.'?'.$flag.'&'.$query);
if ($body === false) {
throw new RuntimeException('WorldTides request failed');
}
$json = json_decode($body, true, flags: JSON_THROW_ON_ERROR);
if (($json['status'] ?? 200) !== 200) {
throw new RuntimeException($json['error'] ?? 'WorldTides error');
}
return $json;
}
}
$tides = new WorldTides('<apiKey>', 33.768321, -118.195617);
$extremes = $tides->getExtremes(2026, 7, 1, 3);
$samples = $tides->getSamples(2026, 7, 1, 3);
class WorldTides {
constructor(key, lat, lon) {
this.key = key;
this.lat = lat;
this.lon = lon;
this.base = "https://www.worldtides.info/api/v3";
}
getExtremes(year, month, day, numDays) {
return this.request("extremes", year, month, day, numDays).then(r => r.extremes ?? []);
}
getSamples(year, month, day, numDays) {
return this.request("heights", year, month, day, numDays).then(r => r.heights ?? []);
}
async request(flag, year, month, day, numDays) {
const date = `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
const params = new URLSearchParams({
date, days: String(numDays), lat: String(this.lat), lon: String(this.lon), key: this.key
});
const response = await fetch(`${this.base}?${flag}&${params}`);
const json = await response.json();
if (!response.ok) throw new Error(`HTTP ${response.status}`);
if ((json.status ?? 200) !== 200) throw new Error(json.error ?? "WorldTides error");
return json;
}
}
const tides = new WorldTides("<apiKey>", 33.768321, -118.195617);
const extremes = await tides.getExtremes(2026, 7, 1, 3);
const samples = await tides.getSamples(2026, 7, 1, 3);
<?php
function worldtides_request($flag, $year, $month, $day, $num_days) {
$url = add_query_arg([
$flag => null,
'date' => sprintf('%04d-%02d-%02d', $year, $month, $day),
'days' => $num_days,
'lat' => 33.768321,
'lon' => -118.195617,
'key' => '<apiKey>',
], 'https://www.worldtides.info/api/v3');
$response = wp_remote_get($url, ['timeout' => 10]);
if (is_wp_error($response)) {
return $response;
}
$json = json_decode(wp_remote_retrieve_body($response), true);
if (($json['status'] ?? 200) !== 200) {
return new WP_Error('worldtides_error', $json['error'] ?? 'WorldTides error');
}
return $json;
}
function getExtremes($year, $month, $day, $num_days) {
$json = worldtides_request('extremes', $year, $month, $day, $num_days);
return is_wp_error($json) ? $json : ($json['extremes'] ?? []);
}
function getSamples($year, $month, $day, $num_days) {
$json = worldtides_request('heights', $year, $month, $day, $num_days);
return is_wp_error($json) ? $json : ($json['heights'] ?? []);
}
using System;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
public sealed class WorldTides {
readonly HttpClient http = new();
readonly string key;
readonly double lat;
readonly double lon;
public WorldTides(string key, double lat, double lon) {
this.key = key;
this.lat = lat;
this.lon = lon;
}
public async Task<object[]> GetExtremes(int year, int month, int day, int numDays) =>
(await Request("extremes", year, month, day, numDays)).extremes ?? [];
public async Task<object[]> GetSamples(int year, int month, int day, int numDays) =>
(await Request("heights", year, month, day, numDays)).heights ?? [];
async Task<Response> Request(string flag, int year, int month, int day, int numDays) {
var date = $"{year:D4}-{month:D2}-{day:D2}";
var url = $"https://www.worldtides.info/api/v3?{flag}&date={date}&days={numDays}&lat={lat}&lon={lon}&key={Uri.EscapeDataString(key)}";
var response = await http.GetAsync(url);
var json = await response.Content.ReadFromJsonAsync<Response>();
if (!response.IsSuccessStatusCode) throw new HttpRequestException($"HTTP {(int)response.StatusCode}");
if ((json?.status ?? 200) != 200) throw new Exception(json?.error ?? "WorldTides error");
return json!;
}
record Response(int status, string? error, object[]? extremes, object[]? heights);
}
public static class App {
public static async Task Main() {
var tides = new WorldTides("<apiKey>", 33.768321, -118.195617);
var extremes = await tides.GetExtremes(2026, 7, 1, 3);
var samples = await tides.GetSamples(2026, 7, 1, 3);
Console.WriteLine($"Extremes: {extremes.Length}");
Console.WriteLine($"Samples: {samples.Length}");
}
}
library(httr2)
library(jsonlite)
WorldTides <- function(key, lat, lon) {
lat_string <- format(lat, scientific = FALSE, trim = TRUE, digits = 15)
lon_string <- format(lon, scientific = FALSE, trim = TRUE, digits = 15)
request <- function(flag, year, month, day, num_days) {
date <- sprintf("%04d-%02d-%02d", year, month, day)
response <- httr2::request("https://www.worldtides.info/api/v3") |>
req_url_query(!!flag := "", date = date, days = num_days, lat = lat_string, lon = lon_string, key = key) |>
req_error(is_error = function(resp) FALSE) |>
req_perform()
if (resp_status(response) >= 300) stop(resp_body_string(response))
json <- jsonlite::fromJSON(resp_body_string(response), simplifyVector = FALSE)
if (!is.null(json$status) && json$status != 200) stop(ifelse(is.null(json$error), "WorldTides error", json$error))
json
}
list(
getExtremes = function(year, month, day, num_days) request("extremes", year, month, day, num_days)$extremes,
getSamples = function(year, month, day, num_days) request("heights", year, month, day, num_days)$heights
)
}
tides <- WorldTides("<apiKey>", 33.768321, -118.195617)
extremes <- tides$getExtremes(2026, 7, 1, 3)
samples <- tides$getSamples(2026, 7, 1, 3)
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
)
type WorldTides struct {
Key string
Lat float64
Lon float64
}
func (w WorldTides) GetExtremes(year, month, day, numDays int) ([]any, error) {
json, err := w.request("extremes", year, month, day, numDays)
return json.Extremes, err
}
func (w WorldTides) GetSamples(year, month, day, numDays int) ([]any, error) {
json, err := w.request("heights", year, month, day, numDays)
return json.Heights, err
}
func (w WorldTides) request(flag string, year, month, day, numDays int) (*Response, error) {
values := url.Values{
"date": {fmt.Sprintf("%04d-%02d-%02d", year, month, day)},
"days": {fmt.Sprint(numDays)},
"lat": {fmt.Sprint(w.Lat)},
"lon": {fmt.Sprint(w.Lon)},
"key": {w.Key},
}
resp, err := http.Get("https://www.worldtides.info/api/v3?" + flag + "&" + values.Encode())
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode >= 300 {
return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
}
var out Response
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
return nil, err
}
if out.Status != 0 && out.Status != 200 {
return nil, fmt.Errorf("%s", out.Error)
}
return &out, nil
}
type Response struct {
Status int `json:"status"`
Error string `json:"error"`
Extremes []any `json:"extremes"`
Heights []any `json:"heights"`
}

