GET https://solarproof.com.au/apidata.php

Description

Returns a lightweight list of project IDs (with their last-modified timestamps) that have changed since the since timestamp you supply. Feed the returned IDs into Get Project to fetch full details for just those projects. On a typical sync run nothing has changed and the response is a single, tiny request.

Replace polling loops with this endpoint. If your integration currently calls getprojectinfo for every project on a timer, one call here tells you which projects actually changed - typically reducing hundreds of requests per run to one or two.
Cursor pattern: every response includes server_time. Store it and pass it as since on your next call - this avoids clock-skew problems because the cursor always comes from the Solar Proof server, never your own clock.
Rate limits: API requests are limited to 1,000 per hour per account. Requests over the limit receive HTTP 429 with a Retry-After header. Integrations using this delta endpoint sit far below the limit by design.

Request Parameters

Query Parameters

Parameter Type Required Description
api_key string Required Your Solar Proof API key
action string Required Must be set to "getprojectssince"
since string | integer Required Return projects modified after this moment. Accepts a datetime string (2026-07-23 09:00:00, server timezone) or unix seconds (1784840400).
Use the server_time value from your previous response.
page integer Optional Zero-based page (default: 0). Each page returns up to 500 projects, ordered oldest change first. If has_more is true, request the next page.

Response Structure

Field Type Description
status integer 200 on success
since string The cutoff the server used (normalised to a datetime)
server_time string Current server time - store this and pass it as since next call
page / per_page / count integer Pagination info; count is the number of projects in this page
has_more boolean true when another page of changes exists
projects array Objects of project_id + last_modified, oldest change first

Example Response

Response Body JSON
{
    "status": 200,
    "since": "2026-07-23 08:00:00",
    "server_time": "2026-07-23 09:00:02",
    "page": 0,
    "per_page": 500,
    "count": 3,
    "has_more": false,
    "projects": [
        { "project_id": 18244, "last_modified": "2026-07-23 08:12:41" },
        { "project_id": 18251, "last_modified": "2026-07-23 08:36:05" },
        { "project_id": 17903, "last_modified": "2026-07-23 08:58:19" }
    ]
}

Example Requests

Basic Request

cURL bash
curl -X GET "https://solarproof.com.au/apidata.php?action=getprojectssince&since=2026-07-23%2008:00:00&api_key=YOUR_API_KEY"

Full Sync Loop (Google Apps Script)

A complete incremental sync suitable for a time-driven trigger - it remembers the server-time cursor between runs and only fetches projects that changed.

Google Apps Script javascript
function syncChangedProjects() {
  var API_KEY = 'YOUR_API_KEY';
  var props   = PropertiesService.getScriptProperties();
  var since   = props.getProperty('sp_sync_cursor') || '2026-01-01 00:00:00';

  var url = 'https://solarproof.com.au/apidata.php'
          + '?action=getprojectssince'
          + '&since=' + encodeURIComponent(since)
          + '&api_key=' + API_KEY;

  var res = JSON.parse(UrlFetchApp.fetch(url).getContentText());

  res.projects.forEach(function (p) {
    // Fetch full detail ONLY for projects that actually changed
    var detailUrl = 'https://solarproof.com.au/apidata.php'
                  + '?action=getprojectinfo&project_id=' + p.project_id
                  + '&api_key=' + API_KEY;
    var detail = JSON.parse(UrlFetchApp.fetch(detailUrl).getContentText());
    // ... write detail.project_info[0] into your sheet ...
  });

  // Store the server-side cursor for the next run (no clock skew)
  props.setProperty('sp_sync_cursor', res.server_time);
}
Python python
import requests

API_KEY = 'YOUR_API_KEY'
BASE = 'https://solarproof.com.au/apidata.php'

def sync(since):
    page = 0
    while True:
        r = requests.get(BASE, params={
            'action': 'getprojectssince',
            'since': since,
            'page': page,
            'api_key': API_KEY,
        }).json()
        for p in r['projects']:
            detail = requests.get(BASE, params={
                'action': 'getprojectinfo',
                'project_id': p['project_id'],
                'api_key': API_KEY,
            }).json()
            # ... upsert detail into your system ...
        if not r['has_more']:
            return r['server_time']   # store as the next 'since'
        page += 1

Notes

  • First call returns everything once. The change-tracking column was introduced in July 2026, so the first delta call with an old since returns all projects; subsequent calls return true deltas.
  • Any saved change counts. A project appears when anything on it is saved - pricing, design, customer, signature status and so on. Deletions are not reported.
  • Ordering is oldest change first, so paging with has_more never skips a project even if changes occur mid-sync.

Related Endpoints