Emergency services raced to extract people from the wreckage of smashed carriages thrown across a single track into olive groves near the town of Andria, in what one witness described as an “apocalyptic scene”.
Coffins were taken to the site near the city of Bari to carry away the first of the dead as 200 rescue workers sifted through the wreckage in temperatures reaching 40 degrees Celsius (104 degrees Fahrenheit).
“I saw dead people, others who were begging for help, people crying. The worst scene of my life,” one policeman told journalists.
Giancarlo Conticchio, head of the railway police for the region, said 22 people had died, and 43 were injured, four of them critically.
“We will be working through the night to search the wreckage for survivors or other victims,” he said.
Italian media put the death toll at 23, saying among the dead were a mother and child, found locked in an embrace.
The high-speed collision happened on a slight bend in the track in open countryside and flung the front carriages of both trains into olive groves bordering the line, slinging bits of metal from the wreckage.
Investigators said at least one of the trains had been travelling very fast, and it was possible the collision was caused by human error.
One of the four-carriage trains was supposed to have waited at a station for a green light before heading down the track between the towns of Corato and Andria. One of the train drivers was confirmed to have died.
“This is a disaster like a plane crash,” Corato mayor Massimo Mazzilli said on Facebook.
“It’s an apocalyptic scene, it was hard not to vomit on first sight,” said local journalist Lucia Olivieri who works for AndriaLive.it.
“There were bodies parts, blood, bits of people,” one elderly lady told local TV channel Telesveva.
“I walked barefoot through the wreckage. I dug under it and managed to pull my husband out,” she said.
The trains were operated by private railway company Ferrotramviaria – just one of the 30 or so private companies which run on small lines criss-crossing Italy in areas not covered by national operator Trenitalia.
Prime Minister Matteo Renzi, who visited the crash site in the evening, said earlier that it was “a moment for tears” and the country would “not stop until we know what happened”.
Pope Francis sent his “fervent prayers for those who have tragically died”.
Messages of condolence and solidarity also came in from overseas, including France and Russia.
Ferrotramviaria said it was not possible to say how many people had been on board the two trains involved in yesterday’s crash, as many passengers had season tickets.
The first victim to be named was policeman Fulvio Schinzari, 53, found by a colleague who was helping the rescue services, Italian media reported.
Local hospitals issued a request on social networks for blood donors to come forward to help the injured.
Paramedics set up an impromptu medical centre among the olive trees, with three helicopters airlifting out the most seriously hurt victims, including a six-year old boy. There were also psychologists on hand to help survivors.
Many of the passengers on one of the trains were students heading to lessons at the University of Bari or travellers on their way to Bari international airport.
Relatives looking for news of their loved ones were being directed to a sports stadium in Andria. The crisis unit said there were a few foreigners among the injured.
The last major rail disaster in Italy was in 2009, when a freight train carrying liquid petroleum gas derailed and exploded, killing 29 people at the station in the town of Viareggio.