For a local solution, GRASS can be scripted to do this:
# extract raster values at our points
# use cubic convolution for interpolation between DEM locations
v.drape in=my_pts out=pts_srtm_elev type=point rast=srtm_dem method=cubic
I ran an extended version of this for one of my use cases and performance of v.drape was no issue at all.
If you load your LineString gpx/geojson
ogr2ogr -f "PostgreSQL" PG:"dbname=" linestring.geojson
and DEM tiff into PostGIS (replace XXXX with EPSG code, eg for WGS84 use 4326)
raster2pgsql -d -C -I -M -s XXXX -t auto dem.tiff dem
you can then use ST_Segmentize to add extra vertices at the resolution of your DEM (gdalinfo's Pixel Size), then ST_DumpPoints to convert the LineString to a bunch of points,
CREATE TABLE line_points AS select (ST_DumpPoints(ST_Segmentize(wkb_geometry, 5))).*, 0.0 AS ele from line;
then ST_Value to get the elevation at each point,
CREATE INDEX ON line_points USING gist (geom);
UPDATE line_points SET ele = (SELECT ST_Value(dem.rast, geom) AS ele FROM dem WHERE ST_Intersects(line_points.geom, dem.rast) ORDER BY ele LIMIT 1);
then update the geometry to include the elevation
UPDATE line_points SET geom = ST_MakePoint(ST_X(geom), ST_Y(geom), ele);
then ST_MakeLine to reconstruct the points with elevation back into a LineString.
CREATE AGGREGATE array_accum (anyelement)
(
sfunc = array_append,
stype = anyarray,
initcond = '{}'
);
CREATE TABLE linez AS SELECT ST_MakeLine(array_accum(geom)) AS geom FROM (SELECT * FROM line_points ORDER BY path) AS line;
Export back to GeoJSON/CSV/etc:
ogr2ogr -f GeoJSON linez.geojson PG: linez
This could be optimised to generate multiple profiles in bulk, so you're only calling each SQL statement once.
Best Answer
To date, the finest freely available Digital Elevation Model is SRTM. It's delivered from multiple sources (e.g. here (download URLs are very standard so it can easily be automated)), and comes in 5x5 degrees tiles. I don't know of any API to query spatial subsets.