message is human-readable and may change; code is stable — switch on it, not on the message text. details is optional and only present when it helps (for example, marketplaceUrl on a paid-script 402).
The /raw endpoint only serves source for free, keyless scripts. Otherwise:
{ "error": { "code": "payment_required", "message": "This is a paid script; source is not available via the API.", "details": { "marketplaceUrl": "https://…" } }}
You can avoid these entirely: a script object only includes a rawUrl when its source is actually servable. Check rawUrl != null (or scriptType === "free" && key === false) before fetching raw.