SkinLabs

    SkinLabs® Platform API

    Build South African skin intelligence into your clinic or product. The SkinLabs Platform API gives clinics, retailers and integrators access to SA-specific skin data: UV and humidity by city, independent product reviews scored for local climate, and an ingredient catalogue built for melanin-rich skin and Highveld-to-coastal conditions.

    GET https://gnkpzijxuciiaamakgzm.supabase.co/functions/v1/skin-weather?city=johannesburg
    
    {
      "city": "johannesburg",  "cityLabel": "Johannesburg",
      "uvNow": 9.4,  "uvMax": 11.2,  "uvPeakAt": "2026-10-03T10:00:00.000Z",
      "humidity": 28,  "tempMax": 27,
      "fetchedAt": "2026-10-03T08:41:12.000Z",  "stale": false,
      "attribution": "Weather data © OpenWeather"
    }

    Response values are illustrative; the shape matches the deployed function.

    What's ready today, and what isn't

    Every endpoint carries one of two labels. Select a label to filter the reference.

    How this was compiled. Endpoint behaviour for skin weather is taken from the deployed function source. Data shapes for proposed endpoints come from the tables and review pipeline that already hold this data. Nothing here claims a proposed endpoint, key or rate limit exists yet.

    Getting started

    Base URL

    EnvironmentBase URLStatus
    Currenthttps://gnkpzijxuciiaamakgzm.supabase.co/functions/v1Live
    Partner v1https://api.skinlabs.co.za/v1Proposed

    Partners should integrate against the v1 domain once it exists. The current URL exposes the hosting project reference and will change if SkinLabs ever migrates projects.

    Authentication

    The live skin weather endpoint is public and needs no credentials. Every v1 endpoint will require a partner key sent as a header, issued per organisation so usage can be metered and revoked independently.

    X-SkinLabs-Key: sk_partner_live_xxxxxxxxxxxxxxxx

    Conventions

    TopicConvention
    FormatJSON request and response bodies, UTF-8.
    MoneySouth African rand. Fields end in _zar, and a currency field reads "ZAR".
    TimeISO 8601 in UTC, e.g. 2026-10-03T10:00:00.000Z.
    Scores0 to 10, one decimal place.
    LocationCity keys only, never coordinates. Snap the user to the nearest supported city before calling.
    CORSBrowser calls to the live endpoint are allowed from any origin.

    Skin weather

    GET/skin-weather?city={city}Live

    Returns today's UV index, UV peak, humidity and forecast high for one South African city. Clinics use it to time SPF reminders and adjust routine advice on high-UV or very dry days. POST with a JSON body of {"city": "…"} is also accepted and behaves identically.

    Supported cities

    City keyLabel
    johannesburgJohannesburg
    pretoriaPretoria
    cape-townCape Town
    durbanDurban
    gqeberhaGqeberha
    bloemfonteinBloemfontein
    east-londonEast London
    polokwanePolokwane
    mbombelaMbombela
    kimberleyKimberley

    Example request

    curl "https://gnkpzijxuciiaamakgzm.supabase.co/functions/v1/skin-weather?city=cape-town"
    const res = await fetch(
      "https://gnkpzijxuciiaamakgzm.supabase.co/functions/v1/skin-weather?city=cape-town"
    );
    const weather = await res.json();
    if (!res.ok) throw new Error(weather.error); // e.g. "unknown_city"
    if (weather.stale) console.info("Cached reading from", weather.fetchedAt);

    Response fields

    FieldTypeDescription
    citystringThe city key you sent.
    cityLabelstringDisplay name for the city.
    uvNownumberUV index right now.
    uvMaxnumberToday's maximum UV index, local day.
    uvPeakAtstring or nullWhen today's UV peak occurs. null once the peak has passed.
    humidityintegerRelative humidity right now, percent.
    tempMaxintegerToday's forecast high, °C.
    observedAtstringWhen the weather provider produced the reading.
    fetchedAtstringWhen SkinLabs last refreshed this city.
    stalebooleantrue when the provider is unavailable and a reading up to 6 hours old is being served instead.
    attributionstringCredit line you must display alongside the data.

    Errors

    HTTPerrorWhat to do
    400unknown_citySend one of the ten city keys above.
    400invalid_bodyThe POST body wasn't valid JSON.
    405method_not_allowedUse GET or POST.
    502weather_unavailableThe provider failed and no reading under 6 hours old exists. Retry later and hide the card meanwhile.
    Skincare tips aren't included. The SkinLabs app turns this reading into advice using each member's own skin profile, which this endpoint never sees. Partners build their own guidance on top of the raw values.

    Product reviews

    GET/v1/reviewsProposed for v1
    GET/v1/reviews/{id}Proposed for v1

    Independent reviews of skincare products sold in South Africa, each scored on efficacy, value, texture and performance in SA climate. Retailers and clinics can show a SkinLabs verdict next to a product they stock.

    Proposed query parameters

    ParameterTypeDescription
    categorystringProduct category, e.g. Moisturiser, Serum, Cleanser, Sunscreen.
    brandstringExact brand name.
    skin_typestringMatches against skin_type_match.
    originstringsouth_africa or global_available_in_sa.
    limit, cursorinteger, stringPagination. Default limit 20, maximum 100.

    Review object

    FieldTypeDescription
    idstringSlug of brand and product name.
    product_name, brandstringProduct identity.
    categorystringSee the category list above.
    local_price_zarnumberPrice at time of review.
    score_efficacy, score_value, score_texture, score_climatenumber0 to 10. score_climate rates performance in SA heat, humidity, sun and dryness.
    verdictstringOne or two sentence editorial verdict.
    key_ingredientsstring[]Two to six actives named in the source.
    skin_type_matchstring[]Skin types the product suits.
    cautionsstring[]Warnings stated by the manufacturer only. Empty when none are stated.
    retailersobject[]{retailer, price_zar, in_stock, url}. Only prices that SkinLabs has read from the retailer and verified are included.
    originstringWhere the brand is from.
    is_sponsoredbooleanSkinLabs has a commercial interest in this product. Partners must show this disclosure.
    community_rating, community_rating_countnumber or null, integerMember star rating snapshot. null until a member rates.
    published_datestringDate the review went live.
    Not included. The members-only long-form review and internal pipeline fields stay out of the partner response. Exposing the long-form review would give away a Glow Insider benefit for free.

    Products and ingredients

    GET/v1/productsProposed for v1
    GET/v1/products/{id}Proposed for v1
    GET/v1/ingredients?search={term}Proposed for v1
    GET/v1/ingredients/{slug}Proposed for v1

    A structured catalogue of products sold in South Africa and the ingredients in them. Integrators can enrich their own product listings, and clinics can look up an active a patient asks about. Ingredient search would reuse the alias-aware search that already powers the Ingredient Checker.

    ResourceContents
    ProductsProduct identity, variants and SkinLabs scores.
    Product ingredientsIngredient lists linked to each product.
    Skin-type fitWhich skin types each product suits.
    Prices by retailerOnly prices read from the retailer's own page and recently verified.
    BrandsBrand name, origin and profile.
    IngredientsProfiles, aliases and cited sources.

    Ingredient compatibility

    POST/v1/interactions/checkProposed for v1

    Send a list of ingredients and get back any pairs that shouldn't be layered, such as a retinoid with a strong acid. This is useful for clinics checking a patient's home routine against an in-clinic treatment.

    POST /v1/interactions/check
    X-SkinLabs-Key: sk_partner_live_…
    Content-Type: application/json
    
    { "ingredients": ["retinol", "glycolic acid", "niacinamide"] }
    {
      "resolved": [
        { "input": "retinol",       "slug": "retinol",       "resolved": true },
        { "input": "glycolic acid", "slug": "glycolic-acid", "resolved": true },
        { "input": "niacinamide",   "slug": "niacinamide",   "resolved": true }
      ],
      "conflicts": [
        { "a": "retinol", "b": "glycolic-acid", "severity": "caution",
          "guidance": "Alternate nights rather than layering." }
      ]
    }

    Shape is a proposal. A pair with no sourced rule returns nothing; it is never inferred.

    SKYNN assessments

    POST/v1/assessmentsProposed for v1
    GET/v1/assessments/{id}/reportProposed for v1

    Would let a clinic submit a patient's questionnaire answers and receive an Advanced AI Dermatology Analysis from SKYNN AI.

    This stays closed until three things are true. The active prompt set must carry a recorded dermatologist sign-off. The evidence the report cites from must be in place. And a POPIA operator agreement must be signed with each clinic, because the request carries a real patient's health information.

    Partner enquiries

    POST/v1/enquiriesProposed for v1

    Would let an integrator's platform hand a clinic or brand lead to SkinLabs, for example a clinic interested in Practice Suite or a brand asking about Spotlight. Until it exists, use Partnerships or Contact us.

    Errors and caching

    Every error response is JSON with an error field holding a stable, machine-readable code. Branch on the code, not on the HTTP message text.

    { "error": "unknown_city" }

    Caching

    Skin weather readings are cached per city on SkinLabs' side and shared by every caller, so partner traffic never multiplies calls to the weather provider. Responses carry Cache-Control: public, max-age=600; cache for up to 10 minutes on your side.

    Proposed v1 rate limits

    TierRequests per minuteRequests per day
    Sandbox301,000
    Clinic12020,000
    Integrator600Agreed per contract

    Over the limit, v1 would return 429 with {"error":"rate_limited"} and a Retry-After header in seconds. These limits are a proposal and are not enforced today.

    Usage rules

    These rules come from obligations SkinLabs already carries. Partners inherit them when they display SkinLabs data.

    RuleWhy
    Show “Weather data © OpenWeather” wherever skin weather appears.Required by the weather provider's licence.
    Show a sponsored label on any review where is_sponsored is true.SkinLabs has a commercial interest in those products, and hiding it would mislead shoppers.
    Don't present scores or verdicts as clinical testing or a diagnosis.SkinLabs scores follow a published editorial method, not lab trials, and its text deliberately avoids naming conditions.
    Send city keys, never precise coordinates.POPIA. SkinLabs neither receives nor stores precise location through this API.
    Don't send patient names, ID numbers or contact details to any endpoint.POPIA. Clinics keep identifying data on their side and use their own reference ID.

    Before v1 opens

    What has to exist before the first partner clinic can use the proposed endpoints. None of this is built yet.

    • A single partner gateway on api.skinlabs.co.za that maps v1 paths to read-only queries.
    • Partner keys: issued per organisation, stored hashed, checked on every request and revocable.
    • Per-key rate limiting by tier.
    • A review that the catalogue data exposed through the gateway contains no member, payment or analytics rows.
    • A POPIA operator agreement, needed before any clinic sends patient questionnaire data to SKYNN.
    • Published partner terms covering attribution, sponsored disclosure, no-diagnosis framing and pricing per tier.
    • A sandbox with seeded catalogue data.

    Interested in early access? Talk to us about partnerships.