City, who had already qualified, needed just a point to be certain of top spot but they fell behind to Andrej Kramaric's 16th-minute penalty.
The hosts then twice hit the woodwork before Sane's long-range free-kick just before half-time drew them level.
And he found the net again 16 minutes after the break as Pep Guardiola's side bounced back from their Premier League loss away at Chelsea on Saturday, while Hoffenheim finish bottom of the table to miss out on the Europa League.
As they had done in three of their previous five group games in this season’s competition, City fell behind.
Aymeric Laporte was punished for clumsily pushing over Benjamin Hubner as he prepared to shoot when Nico Schulz's scuffed effort dropped into his path, and Kramaric - who had earlier been denied by a brilliant Ederson save - stroked the spot-kick down the middle.
Gabriel Jesus headed against a post from five yards out following a corner before Ilkay Gundogan's free-kick was diverted onto the crossbar by Joelinton's header as City pushed for a response.
An equaliser finally arrived from another dead-ball situation as Sane's attempt from a direct free-kick flew into the net from 30 yards out.
City continued to threaten from set-pieces after the resumption and Laporte should have atoned for giving away the earlier penalty but goalkeeper Oliver Baumann denied both his initial header and then also a follow-up attempt from point-blank range.
Guardiola's men were guilty of an even worse miss when Hoffenheim left no one back and Raheem Sterling, Sane and Bernardo Silva led a three-man breakaway but failed to apply the finishing touch, Baumann denying the latter.
Yet they did find the net via a counter-attacking move just after the hour mark, Sane collecting a return pass from Sterling to poke home a left-footed finish, despite appearing to take one touch too many as Baumann closed him down.
Sterling should have added a third late on but missed the target when found by Phil Foden's cross.
What does it mean? City finish top, but need to ditch their habit of conceding first
The reigning Premier League champions got the job done but only after falling behind yet again, as they had done on German soil in the return fixture and in each of their two meetings with fellow qualifiers Lyon. That is a habit they need to kick if they are to make it to the latter stages of the competition this season.
Sane hitting his stride
He had only scored once before October 20, but Germany international Sane has now found the net nine times since that date for club and country. With Sergio Aguero out injured and Jesus misfiring, the forward's hot streak has been a welcome boost for Guardiola.
A night to forget for Laporte
The reason City went a goal down was due to Laporte's foolish decision to bundle over Hubner in the first half. The City defender then missed two great chances to put his team ahead in the second half but failed to beat Baumann from close range on both occasions.
Key Opta Facts
- Pep Guardiola has led his side to first place in the Champions League group stages for the ninth time in 10 seasons.
- Hoffenheim have both scored and conceded in all 14 of their games in all European competitions.
- The average age of Manchester City's starting XI was 24 years and 95 days, the youngest for an English side in the Champions League since Manchester United against CFR Cluj in December 2012 (24y 54d).
- Hoffenheim are the first side to finish bottom of their Champions League group despite scoring 10+ goals since Olympiacos in 2002-03 (11 goals).
- Leroy Sane is the first German to score twice against German opponents in the Champions League since Carsten Jancker for Bayern Munich against Kaiserslautern in March 1999.
- Andrej Kramaric has scored in each of his last four games in the Champions League for Hoffenheim (5 goals).
City have the chance to return to the top of the Premier League table when they host Everton in an early kick-off on Saturday. Hoffenheim, who are seventh in the Bundesliga, host Borussia Monchengladbach.