Securing public interfaces
The basics
Authentication on every route, including the ones that look uninteresting. An authorization check on the resource and not just on the connection. A rate limit per consumer. And a limited request size, including nesting depth.
Quiet exposure
Lists returning more fields than needed, a result count that reveals existence, and messages distinguishing “doesn't exist” from “no permission.” All three allow mapping the system without breaching anything.
Return only what's needed, and keep refusal behaviour consistent.
Managing keys for consumers
A key per consumer, defined permissions, immediate revocation ability, and usage logging. And a document that states explicitly what's allowed with the interface and what isn't.
Going deeper
Log rejected requests and alert on patterns: route scanning, sequential-identifier attempts, an unusual rate from one source. Most exploitation attempts appear in that noise long before they succeed.